一淘网数据部数据工程-JS系列2之玩转数据类型

本博客会陆续更新一淘数据部 各位技术同学分享的资料。
本次分享的内容来自 霍雍 同学:
受众:
JavaScript公开课是系统学习JavaScript语言的课程。前端同学或对系统学习JavaScript感兴趣的同学均适合查看。
简介:

JavaScript公开课》是JS编程语言的系列分享。

本次分享将对数据类型及转换、类型检测、常用操作符等做由浅入深的探讨。
大纲:
1). 上节回顾;
2). 基础知识;
3). 包装对象;
4). 类型检测;
5). 动手实践;

文件下载:JS系列2之玩转数据类型 上

JS系列2之玩转数据类型 下

一淘网数据部数据工程-JS系列1之认识JavaScript

本博客会陆续更新一淘数据部 各位技术同学分享的资料。
本次分享的内容来自 霍雍 同学:
受众:
JavaScript公开课是系统学习JavaScript语言的课程。前端同学或对系统学习JavaScript感兴趣的同学均适合查看。
简介:

在逐项深入前,第一节是对语言整体的介绍。都说“万事开头难”,而JavaScript却是“入门容易、精通较难”的奇妙语言。
本节我们探讨的主题:
1). 介绍JavaScript;
2). 误解最多的语言 ;
3). 六个引子;
3.1). 类型 & 类型强制转换;
3.2). 操作符;
3.3). 对象 & 基元类型;
3.4). 函数 & 构造器;
3.5). 闭包;
3.6). 原型;

文件下载:JS系列1之认识JavaScript

一淘网数据部数据工程-Sphinx应用开发

本博客会陆续更新一淘数据部 各位技术同学分享的资料。
本次分享的内容来自 征宇同学:
受众:
对sphinx感兴趣的同学,需要论坛搜索引擎的同学
简介:
Sphinx是一个基于SQL的全文检索引擎。这此分享主要针对Sphinx的配置,应用开发进行讲解,让大家对Sphinx有个感性认识

文件下载:SPhinx在量子知道中的应用-PDF文件

网站统计中的数据收集原理及实现

原文:http://www.codinglabs.org/html/how-web-analytics-data-collection-system-work.html

网站数据统计分析工具是网站站长和运营人员经常使用的一种工具,比较常用的有谷歌分析百度统计腾讯分析等等。所有这些统计分析工具的第一步都是网站访问数据的收集。目前主流的数据收集方式基本都是基于javascript的。本文将简要分析这种数据收集的原理,并一步一步实际搭建一个实际的数据收集系统。

(更多…)

JSinDeep1:探索执行环境 (Execution Context)-ES3篇

在我们写JavaScript代码时会定义一些变量、函数等。解释器在执行这些代码时是如何处理并找到我们定义的这些数据的?在程序执行时,引用这些变量等操作的背后都发生了什么?本文主要探讨ECMA-262-3标准中的执行环境(Execution Context)及与之相关的一些内部机制和模型。

(更多…)

使用nginx记日志

使用nginx记日志

做web服务和应用的时候,很多场景下需要记录日志。
如 访问日志,性能分析日志,打点日志,数据统计日志等。

假设有以下主机设置

server {
    listen       80;
    server_name abc.cc;
    root /etc/www/abc;

    access_log /var/log/www/abc/access.log;
    location / {
        index  index.htm index.htm;
    }
}


默认情况下,access_log 会使用 combined 的配置来记录访问日志

log_format combined '$remote_addr - $remote_user [$time_local]  '
           '"$request" $status $body_bytes_sent '
           '"$http_referer" "$http_user_agent"';


通常这样就足够了。

如果是为了更加方便的日志分析,通常我们会使用特殊字符(如 ^A) 来作为日志字段的分隔符,
这样无论是过滤还是排序都会十分方便。甚至可以直接导入 mysql/hive 中,使用强大的 sql 来做查询分析。

为了排版方便,所有特殊字符都使用了展开的写法,请自行替换 ^A 为 ctrl+v,ctrl+a (nginx 日志格式不支持 1 的写法)。

自定义日志格式:

server {
    listen       80;
    server_name abc.cc;
    root /etc/www/abc;

    # 更多日志可用字段(基本上都是 nginx 的变量),见
    # http://wiki.nginx.org/NginxHttpLogModule#log_format
    # http://wiki.nginx.org/NginxHttpUpstreamModule#Variables
    # http://wiki.nginx.org/NginxHttpCoreModule#Variables
    log_format abc "$remote_addr^A$remote_user^A$time_local^A$request_method^A$uri^A$args^A$server_protocol"
        "^A$status^A$body_bytes_sent^A$http_referer"
        "^A$http_user_agent";
    access_log /var/log/www/abc/access.log abc;
    location / {
        index  index.htm index.htm;
    }
}


当把日志使用 ^A 分割以后,后续就可以使用 sort 和 grep 之类工具对特定url做分析了,
比如统计各url请求量倒排取前50个


awk -F^A '{print $5}' /var/log/www/abc/access.log | sort | uniq -c | sort -nr | head -50


有时候可能想对记录的字段做一些处理,比如 $arg_q 可能是搜索关键词,记录的时候如果 unescape 一下,
会更方便分析,存储上也会更小,
那么可以使用 NginxHttpSetMiscModule 模块提供的指令( http://wiki.nginx.org/NginxHttpSetMiscModule#set_unescape_uri )实现:


set_unescape_uri $q $arg_q;

log_format abc "$q";


有时候,我们需要对字段做 hash 转换,可以使用 HttpMapModule 提供的功能(http://wiki.nginx.org/HttpMapModule)


# 需要放到 http 里面,不能放到 server 里 :)
# 根据 url 地址计算分类,便于后续统计
# 具体根据需求做变换就好了
# 第一列是匹配规则,后面的是赋值 ~ 开头的匹配规则是正则
map $uri $typ {
  default               -;

  ~/login              user;
  ~/my                 user;
  ~/static             static;
}

log_format abc "$typ^A$uri";


如果使用 nginx 比较多,可能会尝试使用 if ,建议不要使用,因为nginx的if比较让人混乱。
如果有更多复杂的字段处理需求,可以使用 ngx_lua (http://wiki.nginx.org/HttpLuaModule)。
ngx_lua 里面操作 nginx 变量


# 实现上面 map 类似的功能
# 用法详见 http://wiki.nginx.org/HttpLuaModule#set_by_lua
# lua 语法见 http://www.lua.org/manual/5.1/manual.html#2.4
set_by_lua $typ "
local uri = ngx.var.uri
local _m = string.match
local v = '-'
if _m(uri, '^/login') then
    v = 'user'
elseif _m(uri, '^/my') then
    v = 'user'
elseif _m(uri, '^/static') then
    v = 'static'
end

return v
";


某些情况下,可能我们的字段处理需要查询缓存(如redis)、数据库(如mysql)等,
这些都是可以使用 ngx_openresty 高效完成的(http://openresty.org/)。
这些功能就不在这一篇详细描述了,后续篇章会补充这些功能。
再描述一些复杂的日志记录功能吧。

有时候我们希望根据请求,来判断是否需要记录这一条日志。
在web的访问日志中这种需求比较少,但是独立的日志收集服务器一般有这样的需求的。
比如我需要判定,请求参数 arg_id 必须存在且为数字的时候我才记录日志,可以这样实现


server {
    listen       80;
    server_name abc.cc;
    root /etc/www/abc;

    log_format abc "$msec^A$args^A$q^A$ie^A$oe^A$ref"
        "^A$http_user_agent";
    access_log off;
    location / {
        # 专门记日志的服务,对非合法请求,直接断开连接 或者根据需求302到自己的站点
        # 但是这种302一般不会被用户看到 可以综合考虑做法
        # 444 的意义见 http://wiki.nginx.org/HttpRewriteModule#return
        return 444;
    }
    location = /i-log {
        internal;

        set_unescape_uri $q $arg_q;
        set_unescape_uri $ie $arg_ie;
        set_unescape_uri $oe $arg_oe;
        set_unescape_uri $ref $arg_ref;

        # 这个很重要,否则不会记录的
        log_subrequest on;

        access_log /var/log/www/abc/access.log abc;
        # 这个指令需要 HttpEchoModule (http://wiki.nginx.org/HttpEchoModule#echo) 的支持
        # 因为这个地址只是为了辅助记录日志,所以不需要返回内容
        echo '';
    }
    location = /1.gif {
        default_type  image/gif;
        access_log off;

        access_by_lua "
local q = ngx.var.arg_q
if q then
    q = ngx.unescape_uri(q)
    if q and #q > 0 then
        ngx.location.capture('/i-log?' .. ngx.var.args)
    end
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";

        # 一般独立记录日志的请求,都会返回一张 1x1 的空白gif图
        empty_gif;
    }
}


利用这个特性,我们甚至可以合并多个记录为1个http请求,在 ngx_lua 内再将多条记录拆分记录到日志文件
比如上例,我门可以添加一个字段 n 来标识有几条记录,并且给 q 之类的参数编号 q_1 .. q_n 。


# GET /1.gif?n=2&q_1=a&q_2=b&ie=gbk&oe=utf8&ref=
# 其他代码不变,只修改 1.gif 的 access_by_lua 为以下代码
local n = ngx.var.arg_n
if n then
    n = tonumber(n)
    if n > 0 then
        local logs = {}

        local prefix = string.format('/i-log?ie=%s&oe=%s&ref=%s&q=', ngx.var.arg_ie, ngx.var.arg_oe, ngx.var.arg_ref)
        while n >= 1 do
            -- 这里可以对 q_n 做进一步的校验,看是否需要记录下来
            table.insert(logs, {prefix .. (ngx.var['arg_q_' .. n] or '')})
            n = n - 1
        end

        -- 见 http://wiki.nginx.org/HttpLuaModule#ngx.location.capture_multi
        ngx.location.capture_multi(logs)
    end
end


如果请求量较大,一把需要添加写buffer,
方式为在每条 access_log 后面添加 buffer=32k 这样的设置(见 http://wiki.nginx.org/NginxHttpLogModule#access_log)
缓存的大小可以设置成 可以忍受丢失的记录数*每条记录的size

至此,各种记录功能都完成啦。
日志服务其他必要的功能,就是日志轮转了。

nginx 日志轮转的原理是

# 启动nginx收日志
# 启用cron任务
## 将日志文件move走 (文件名可以带上时间戳)
## 给 nginx master 进程发送 USR1 信号(nginx就会重新打开新的日志文件)
### 否则,日志会仍然记录到之前的日志文件中,虽然被 move 了
## cron 的频度可以根据日志大小调整,尽可能大些,能以天为单位就不要以半天为单位

具体实现可见 http://jk.aiwaly.com/wp/nginx-cut-log.html

以上操作中涉及较多 nginx 扩展模块,如果不想折腾,可以使用 ngx_openresty (http://openresty.org)
如果是淘宝系的同学,可以旺旺联系 定球 ,我们有现成的 rpm 可以使用:)

使用中有任何问题,欢迎发信到 kindy61  或者 微博 @定球呀球