2013年10月

OpenCDN流量采集bug修复

OpenCDN节点端Nginx把节点流量信息以下面这种格式输出到管道bandwidth.pipe。

nginx.conf
log_format  bandwidth   '[$time_local] $host $body_bytes_sent $upstream_cache_status';

vhost配置
access_log /usr/local/opencdn/pipe/bandwidth.pipe bandwidth;

在管道的另一段读取流量信息,然后输出到下面的文件里(shell守护)
/usr/local/opencdn/stream/域名/
-| year_month_day_hour_minute
比如
/usr/local/opencdn/stream/www_firefoxbug_net
2013_Sep_30_12_10

管控端采集流量就是通过守护,以http://%s:%s/ocdn/stream?token=%s&domain=%s来抓取流量,处理后导入数据库。

之前OpenCDN产生一个小需求,使用CDN加速的小伙伴们添加了一个域名比如是aaa.com,但是他还加了别名,xxx.aaa.com(可以是二级域名)。OpenCDN节点端会在Nginx的vhost配置server_name加一个aliasname。但是小伙伴访问都是用这个aliasname访问的。而根据上面的逻辑管控端就没有办法采集到aliasname的流量。
怎么办?在节点端加对域名的流量路径加个软链接就可以,那么aliasname访问的流量也会记录到原站点的流量下。

中间还遇到一个小问题

$ mkdir test1
$ ln -s test1 test2
$ [ -L test2 ] ; echo $?
0
$ [ -L test2/ ] ; echo $?
1

为什么测试目录链接文件加/和没加/有区别呢?得@smallfish指点,大概明白了。软链接是新建一个inode节点的,这个inode节点对应的block记录的是原目录的路径,从软链接到原目录路径其实是比正常访问多一层的。我想linux的设计就是加/来区别吧,没加/那么就是读取普通的目录文件一样,加了/就是change到原路径里去了。

Python QQ纯真库解析

QQWry.Dat不是普通的txt文件,所以python解析要特定的程序。下面是一个Python类

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# author : firefoxbug
# E-Mail : wanghuafire@gmail.com
# Blog   : www.firefoxbug.net

import sys
import socket
from struct import pack, unpack

class IPInfo(object):
    '''QQWry.Dat数据库查询功能集合
    '''
    def __init__(self, dbname):

        self.dbname = dbname
        f = file(dbname, 'r')
        self.img = f.read()
        f.close()

        (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8])
        self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1

    def getString(self, offset = 0):
        o2 = self.img.find('\0', offset)
        gb2312_str = self.img[offset:o2]
        try:
            utf8_str = unicode(gb2312_str,'gb2312').encode('utf-8')
        except:
            return '未知'
        return utf8_str

    def getLong3(self, offset = 0):
        s = self.img[offset: offset + 3]
        s += '\0'
        return unpack('I', s)[0]

    def getAreaAddr(self, offset = 0):
        ''' 通过给出偏移值,取得区域信息字符串,'''

        byte = ord(self.img[offset])
        if byte == 1 or byte == 2:
            p = self.getLong3(offset + 1)
            return self.getAreaAddr(p)
        else:
            return self.getString(offset)

    def getAddr(self, offset, ip = 0):
        img = self.img
        o = offset
        byte = ord(img[o])

        if byte == 1:
            return self.getAddr(self.getLong3(o + 1))

        if byte == 2:
            cArea = self.getAreaAddr(self.getLong3(o + 1))
            o += 4
            aArea = self.getAreaAddr(o)
            return (cArea, aArea)

        if byte != 1 and byte != 2:

            cArea = self.getString(o)
            o = self.img.find('\0',o) + 1
            aArea = self.getString(o)
            return (cArea, aArea)

    def find(self, ip, l, r):
        ''' 使用二分法查找网络字节编码的IP地址的索引记录'''
        if r - l <= 1:
            return l

        m = (l + r) / 2
        o = self.firstIndex + m * 7
        new_ip = unpack('I', self.img[o: o+4])[0]
        if ip <= new_ip:
            return self.find(ip, l, m)
        else:
            return self.find(ip, m, r)

    def getIPAddr(self, ip):
        ip = unpack('!I', socket.inet_aton(ip))[0]
        i = self.find(ip, 0, self.indexCount - 1)
        o = self.firstIndex + i * 7

        o2 = self.getLong3(o + 4)

        (c, a) = self.getAddr(o2 + 4)
        return (c, a)

    def output(self, first, last):
        for i in range(first, last):
            o = self.firstIndex +  i * 7
            ip = socket.inet_ntoa(pack('!I', unpack('I', self.img[o:o+4])[0]))
            offset = self.getLong3(o + 4)
            (c, a) = self.getAddr(offset + 4)
            print "%s %d %s/%s" % (ip, offset, c, a)

'''search ip and its'localtion '''
def search_ip(ip_instance,ipaddr):
    if ipaddr :
        try :
            (c, a) = ip_instance.getIPAddr(ipaddr)
            print '%s/%s'%(c,a)
        except :
            print 'Unknow/Unknow'

if __name__ == '__main__':
    ip_instance = IPInfo('./QQWry.Dat')
    search_ip(ip_instance,"8.8.8.8")

纯真库优势就是可以精确到公司,街道,网吧,但是有麻烦的一点就是统计起来不方便,因为都是一个字符串,需要再做分词。

理解inode

转自 http://www.ruanyifeng.com/blog/2011/12/inode.html

inode是一个重要概念,是理解Unix/Linux文件系统和硬盘储存的基础。

我觉得,理解inode,不仅有助于提高系统操作水平,还有助于体会Unix设计哲学,即如何把底层的复杂性抽象成一个简单概念,从而大大简化用户接口。

下面就是我的inode学习笔记,尽量保持简单。

- 阅读剩余部分 -

logstash+kibana3 GeoIP地理位置

原日志从logstash输入,根据filter匹配或者修改,通过json输出到elasticsearch,然后到kibana3。所以要显示地理位置,要在logstash里面设置。logstash的jar包默认应该是有GeoIP的包的。下面是配置,pattern主要是针对Nginx的combined日志格式

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

filter {
grok {
        type => "linux-syslog"
        pattern => "%{IPORHOST:source_ip} - %{USERNAME:remote_user} \[%{HTTPDATE:time_local}\] %{QS:request} %{INT:status} %{INT:body_bytes_sent} %{QS:http_referer} %{QS:http_user_agent}
}
geoip {
   source => "source_ip"
   type => "linux-syslog"
   add_tag => [ "geoip" ]
#   database => "/var/geoip/GeoLiteCity.dat" 不是必须
}
}

kibana5

- 阅读剩余部分 -

Python读取fifo管道

Shell读取fifo的管道是很方便的,每一次read的时候都可以阻塞,只要管道两边都对接好。但Python在读取管道的时候我就一直没有找到好的解决方案,也是想每次读取fifo里面的buffer,没有buffer就阻塞着。下面一段代码可以实现这个功能,但是还是感觉很笨。

<pre>
pipe = "test.fifo"
...
read_cmd = "read -r a < " + pipe +";echo \"$a\""
lines = os.popen(read_cmd).read().split("\n")
line_str = lines[0]
</pre>

logstash+kibana3搭建

之前玩过一段时间的logstash+kibana3,还有elasticsearch。但是困于libana安装环境的复杂,自从kibana3出来之后就方便了很多,对于logstash的版本也会有一些问题。

logstash


yum install java-1.6.0*
wget https://download.elasticsearch.org/logstash/logstash/logstash-1.2.1-flatjar.jar
mkdir -p /usr/local/bin/logstash/
mkdir -p /etc/logstash/
cd /usr/local/bin/logstash/ && ln -s ./logstash-1.2.1-flatjar.jar ./logstash.jar

编辑logstash的配置文件

- 阅读剩余部分 -

Nginx反代内容替换

Nginx做反向代理的时候因为流量都是从Nginx过,所以有时候会有需求修改页面,替换个元素,标签,logo等等。这个功能可以用ngx_http_subs_filter_module来解决。

具体的安装过程和普通的三方模块一样

- 阅读剩余部分 -

Linux Cache和Buffers

之前在写日志模块的sql语句处理模块过程中,大概一次性插入一万多条数据到Mysql,结果top出来used内存很大,而且一直都不释放那部分内存,就很奇怪一直没有找到问题,开始还以为python哪部份内存没释放好,但是总觉得释放内存是python自己搞定的。也测试过Mysql连接数,都是正常。后来请教了下大牛,才知道原来top出来还是大有文章的。

- 阅读剩余部分 -