Archive for June, 2013

作者:AngryFox 分类: Uncategorized June 30th, 2013 暂无评论

目前最好的python模块机器学习类工具包,包括机器学习、数据挖掘、自然语言处理、网络分析、web嗅探。
Basics:

numpy – numerical library, numpy.scipy.org/
scipy – Advanced math, signal processing, optimization, statistics, www.scipy.org/
matplotlib, python plotting – Matplotlib, matplotlib.org

Machine Learning and Data Mining:

MDP, a collection of supervised and unsupervised learning algorithms, pypi.python.org/pypi/MDP/2.4
mlpy, Machine Learning Python, mlpy.sourceforge.net
NetworkX, for graph analysis, networkx.lanl.gov/
Orange, Data Mining Fruitful & Fun, biolab.si Pandas
pandas, Python Data Analysis Library, pandas.pydata.org
pybrain, pybrain.org
scikits-learn – Classic machine learning algorithms – Provide simple an efficient solutions to learning problems, scikit-learn.org/stable/

Natural Language:

NLTK, Natural Language Toolkit, nltk.org

For web scraping:

Scrapy, An open source web scraping framework for Python scrapy.org
urllib/urllib2

作者:AngryFox 分类: Uncategorized June 28th, 2013 暂无评论

在进行配置之前,我们首先要知道什么是Prefork模式,什么是Worker模式,什么是Event模式,以及什么是MPM。

MPM是Apache2引入的一个概念,就是将结构模块化。把核心任务处理作为一个可插拔的模块,即MPM,使其能针对不同的环境进行优化。在这个情况下,就诞生出了处理模式的概念。处理模式现在分为Prefork、Worker、Event三种。

Prefork MPM基于非线程模型,和Apache 1.x版本中的处理方式很相似。Prefork MPM在所有情况下都很安全,对运行非线程安全(non-thread-safe)模式的软件如PHP,它是唯一的安全选择。对于某些应用程序,包括在 Apache 1.3上非常流行的程序(如简单静态页面、CGI脚本等),Prefork MPM是最好的选择。另一方面,prefork用单独的子进程来处理不同的请求,进程之间是彼此独立的,这也使其成为最稳定的MPM之一。但是由于每一个请求都会产生一个新的进程,导致系统资源(尤其是内存)消耗的很快,一旦并发量较大的时候,大量的Apache进程会占用巨大的内存空间。

而Worker MPM基于线程模式,具有内存消耗低(对繁忙的服务很重要)、扩展性在某些特定应用情况下比Prefork更好等优点。在这个模式下,采用的进程和线程混合的形式处理请求。由于使用线程来处理,所以可以处理相对海量的请求,而系统资源的开销要小于基于进程的Prefork模式。

以上两种稳定的MPM方式在非常繁忙的服务器应用下都有些不足。尽管HTTP的Keepalive方式能减少TCP连接数量和网络负载,但是 Keepalive需要和服务进程或者线程绑定,这就导致一个繁忙的服务器会耗光所有的线程。Event MPM是解决这个问题的一种新模型,它把服务进程从连接中分离出来。在服务器处理速度很快,同时具有非常高的点击率时,可用的线程数量就是关键的资源限 制,此时Event MPM方式是最有效的。一个以Worker MPM方式工作的繁忙服务器能够承受每秒好几万次的访问量(例如在大型新闻服务站点的高峰时),而Event MPM可以用来处理更高负载。值得注意的是,Event MPM不能在安全HTTP(HTTPS)访问下工作。

一目了然,三种MPM模式各有各的优缺点。但是如果我们经常遇到访问量一大,服务器资源就吃紧的情况,那么就是Prefork模式瓶颈了。在其他两类MPM中,通用的做法还是使用Worker模式来解决问题。Event MPM由于不支持安全连接(HTTPS)所以导致应用有一定的局限性。

与Apache一样,他的进程数也是可以根据设置分为动态和静态的。
而php-fpm也是同样存在两种方式,一种是直接开启指定数量的php-fpm进程,不再增加或者减少;另一种则是开始的时候开启一定数量的php-fpm进程,当请求量变大的时候,动态的增加php-fpm进程数到上限,当空闲的时候自动释放空闲的进程数到一个下限。

这两种不同的执行方式,可以根据服务器的实际需求来进行调整。

这里先说一下涉及到这个的几个参数吧,他们分别是pm、pm.max_children、pm.start_servers、pm.min_spare_servers和pm.max_spare_servers。

pm表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。在更老一些的版本中,dynamic被称作apache-like。这个要注意看配置文件给出的说明了。

下面4个参数的意思分别为:

pm.max_children:静态方式下开启的php-fpm进程数量。
pm.start_servers:动态方式下的起始php-fpm进程数量。
pm.min_spare_servers:动态方式下的最小php-fpm进程数量。
pm.max_spare_servers:动态方式下的最大php-fpm进程数量。
如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启设置的数量个php-fpm进程。

如果dm设置为dynamic,那么pm.max_children参数失效,后面3个参数生效。系统会在php-fpm运行开始的时候启动pm.start_servers个php-fpm进程,然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。

那么,对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache一样,我们运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。所以,动态方式因为会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或者VPS上使用。具体最大数量根据 内存/20M 得到。比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比较合适的值在5~10之间。

然后对于比较大内存的服务器来说,设置为静态的话会提高效率。因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M 得到。比如说2GB内存的服务器,可以设置为50;4GB内存可以设置为100等。

作者:AngryFox 分类: Uncategorized June 26th, 2013 暂无评论
lua文件不能注释文件最后一行
lua模块/包;包库为Lua提供简易的加载及创建模块的方法,由require、module方法及package表组成
 1、module (name [, ···])
  功能:建立一个模块。
  当package.loaded[name]中存在时,当中的表作为module;
  当在全局表中存在name指定的表时,此表作为module;
  当以前两种情况都不存表name时,将新建一个表,并使其作为全局名name的值,并package.loaded[name],而且设t._NAME为name,t._M为module,t._PACKAGE为包的全名(模块名-组件a.b.c);最后把此module设t作为当前函数的新环境表和package.loaded[name]的新值(也就是说,旧的环境表将不能访问,除了加上package.seeall参数外),以被require使用
  module(name)后的可选参数为接收module名的函数,如package.seeall
  2、require (modname)
  功能:加载指定的模块。
  此函数先检测package.loaded表中是否存在modname,存在则直接返回当中的值,没有则通过郰定义的加载器加载modname。
  查找加载器顺序:
  (1)检测package.preload表是否存在modname,有则加载
  (2)通过Lua Loader加载,通过查找存放于package.path的路径加载,有则加载
  (3)通过C Loader加载,通过查找存放于package.cpath的路径加载,有则加载
  (4)通过all-in-one Loader加载:
  通过查找modname.dll并查找当中的luaopen_
  其中XXXX为载块名-后的字符用_替换.后的字符:如:a.v1-b.c 当函数名为luaopen_b_c
  当require查找的不是一个Lua库或C库,它就会调用all-in-one loader,此加载器是用C路径作为载块的目录,
  当查找到合适的加载器时,require就会加载其中的模块,当加载器有返回值,将会存放于package.loaded[modname]表。最后返回package.loaded[modname]表
  当加载失败时,require将触发错误
  3、package.cpath
  功能:用于require C loader的搜索路径
  可以通过修改LUA_CPATH变量(luaconf.h)修改此值
  4、package.loaded
  功能:一个用于让require知道哪些模块已加载的记录表,如果package.loaded已经有require要的值,则直接返回此值
  5、package.loadlib (libname, funcname)
  功能:通过动态连接C函数库方式加载Lua扩展库
  libname为库文件名,funcname为入口函数(此函数必须为纯C接口函数 c++则需用 extern "C" {} 进行限制)
  6、package.path
  功能:用于require Lua loader的搜索路径
  可以通过修改LUA_PATH变量(luaconf.h)修改此值
  7、package.preload
  功能:一个用于保存特殊模块加载器的表
  8、package.seeall(module)
  功能:为module设置一个元表,此元表的__index字段的值为全局环境_G。所以module可以访问全局环境
  注:以此函数作为module()的一个选项(详细见module())

默认先从环境变量LUA_PATH中搜索 lua文件,如果搜索不到,则从LUA_CPATH中搜索C文件。
--将自定义包路径加入package的 搜索路径中。也可以加到环境变量LUA_PATH中
local p = "自定义包路径的父目录"
local m_package_path = package.path
package.path = string.format("%s;%s?.lua;%s?/init.lua",
    m_package_path, p, p)
开头都有这么一行代码module(..., package.seeall),这是为了模块名以文件名命名,还有为了不污染全局变

量_G,返回当前模块提供的函数等等。相当于:
--将模块名设置为文件名,且加载这个模块
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
--引入所需模块
local io = io
local print = print
setfenv(1, M)          --> 设置当前环境变量
--开始实现模块功能
function test()
    print("test")
end

lua中,用require来加载包,如果想重命名加载的包,可以如下操作:
local t = require "demopackage", 这时候,就可以用t调用demopackage中的功能了。
ua加载包时,只加载程序中使用到的函数。

1. string库中所有的字符索引从前往后是1,2,...;从后往前是-1,-2,...
2. string库中所有的function都不会直接操作字符串,而是返回一个结果
s = "[abc]"
string.len(s)        <==返回5
string.rep("abc", 2) <==返回"abcabc"
string.lower("ABC") <==返回"abc"
string.upper("abc") <==返回"ABC"
string.sub(s, 2)     <==返回"abc]"
string.sub(s, -2)    <==返回"c]"
string.sub(s, 2, -2) <==返回"abc"
string.format(fmt, ...)返回一个类似printf的格式化字符串
string.find(s, pattern, pos)
第1个参数:源字符串
第2个参数:待搜索之模式串
第3个参数:A hint, 从pos位置开始搜索
找到匹配返回:匹配串开始和结束的位置,否则返回nil
简单的模式串
s = "hello world"
i, j = string.find(s, "hello")
print(i, j) --> 1 5
print(string.sub(s, i, j)) --> hello
print(string.find(s, "world")) --> 7 11
i, j = string.find(s, "l")
print(i, j) --> 3 3
print(string.find(s, "lll")) --> nil
格式化的模式串
s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date))) --> 30/05/1999
下面的表列出了Lua支持的所有字符类:
. 任意字符
%s 空白符
%p 标点字符
%c 控制字符
%d 数字
%x 十六进制数字
%z 代表0的字符
%a 字母
%l 小写字母
%u 大写字母
%w 字母和数字
上面字符类的大写形式表示小写所代表的集合的补集。例如,'%A'非字母的字符:
模式串中的特殊字符
( ) . % + - * ? [ ^ $
'%' 用作特殊字符的转义字符
'%.' 匹配点;
'%%' 匹配字符 '%'。
转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。当对一个字符有疑问的时候,为安

全起见请使用转义字符转义他。
用'[]'创建字符集
'[%w_]' 匹配字母数字和下划线
'[01]' 匹配二进制数字
'[%[%]]'匹配一对方括号
在'[]'中使用连字符'-'
'%d'    表示 '[0-9]';
'%x'    表示 '[0-9a-fA-F]'
'[0-7]' 表示 '[01234567]'
在'[]'开始处使用 '^' 表示其补集:
'[^0-7]' 匹配任何不是八进制数字的字符;
'[^\n]' 匹配任何非换行符户的字符。
'[^%s]' == '%S'
模式修饰符
+ 匹配前一字符1次或多次
* 匹配前一字符0次或多次;最长匹配
- 匹配前一字符0次或多次;最短匹配
? 匹配前一字符0次或1次
^ 匹配字符串开头
$ 匹配字符串结尾
捕获:用()将要捕获的部分包围起来
pair = "name = Anna"
firstidx, lastidx, key, value = string.find(pair, "(%a+)%s*=%s*(%a+)")
print(key, value) <== name Anna
拷贝捕获(%1-%9)
s = "abc \"it\'s a cat\""
_,_,_,q = string.find(s, "([\"'])(.-)%1"))
print(q) <== it's a cat 如果%d代表第几个捕获的拷贝。
string.gsub(s, pattern, reps)
第1个参数:源字符串
第2个参数:待替换之模式串
第3个参数:替换为reps
将s中所有符合pattern的字串替换为reps,返回结果串+匹配数
print(string.gsub("hello, world", "o", "a"))       <== hella, warld        2
gsub也可以用拷贝捕获技巧
print(string.gsub("hello, world", "(o)", "%1-%1")) <== hello-o, wo-orld    2
print(string.gsub("hello Lua", "(.)(.)", "%2%1")) <== ehll ouLa           4
function trim (s) return (string.gsub(s, "^%s*(.-)%s*$", "%1")) end <== 注意匹配数用括号丢弃
string.gsub(s, pattern, func)
第3个参数:自定义函数,对找到的匹配操作,并传出替换值
s, n = string.gsub("hello world", "l+", function(s) return "xxx" end)
print(s, n) <== hexxxo worxxxd 2
string.gfind(s, pattern)
返回一个迭代器,迭代器每执行一次,返回下一个匹配串;
iter = string.gfind("a=b c=d", "[^%s+]=[^%s+]")
print(iter()) <== a=b
print(iter()) <== c=d
通常用于泛性for循环,下面的例子结果同上
for s in string.gfind("a=b c=d", "[^%s+]=[^%s+]") do
print(s)
end
作者:AngryFox 分类: Uncategorized June 26th, 2013 暂无评论
local P = {}
setmetatable(P, {__index = _G})
setfenv(1, P)
function add ()
 print("this is test!")
end
add()
P.add()
setfenv(1, {}) --改变了函数运行的环境

print("test!")
运行结果:
>C:\Lua\5.1\lua.exe "hello.lua"
C:\Lua\5.1\lua.exe: hello.lua:11: attempt to call global 'print' (a nil value)
stack traceback:
	hello.lua:11: in main chunk
	[C]: ?
this is test!
this is test!
>Exit code: 1

Lua中的全局变量不需要声明就可以使用。由于Lua将全局变量存放在一个普通的table中,则可以通过元表来改变全局变时的行为。
lua5后,允许每个函数拥有自己的环境来查找全局变量。可以通过setfenv来改变一个函数的环境。该函数是一个函数和一个新的环境table。第一个参数除了可以指定为函数本身,还可以指定为一个数字,以表示当前函数调用栈中的层数。数字1表示当前函数,数字2表示调用当前函数的函数,依此类推。
–这里会出错,找不到print ,因为现在函数运行的环境为一个{}

function factory()
	return function()
		return a
	end
end

a = 3
f1 = factory()
f2 = factory()
print(f1()) -->3
print(f2()) -->3
setfenv(f2,{a=10})
print(f1()) -->3
print(f2()) -->10
运行结果:
>C:\Lua\5.1\lua.exe "hello.lua"
3
3
3
10
>Exit code: 0

当setfenv(f1,{a=10})时,就改变了f1的环境,在新环境中a为10。这期间f2的环境并未受到影响。

作者:AngryFox 分类: Uncategorized June 24th, 2013 暂无评论
[/c]tb1 = {"x","y","z"}
print(table.concat(tb1,":"))
print(table.concat(tb1, nil, 1, 2))
print(table.concat(tb1,'\n', 2, 3))
print(table.maxn(tb1))
print(table.sort(tb1))
print(tb1)
--~ foreachi(tb1,print)
table.foreachi(tb1,function(i,v) print(i,v) end)
--~ 不能有空值
table.remove(tb1, nil)
t1 = {2, 4, 6, 8, 10, 12};
--~ foreachi(t1, print)
--~ lua: word.lua:13: attempt to call global 'foreachi' (a nil value)
print(table.getn(tb1))

--~ Keepalived使用的vrrp协议方式,虚拟路由冗余协议 (Virtual Router Redundancy Protocol,简称VRRP);
--~ Heartbeat是基于主机或网络的服务的高可用方式;
--~ keepalived的目的是模拟路由器的双机
--~ heartbeat的目的是用户service的双机
--~ lvs的高可用建议用keepavlived
--~ 业务的高可用用heartbeat
--~ Lua中有8个基本类型分别为:nil、boolean、number、string、userdata、function、thread和table
print(type("Hello world"))
print(type(nil))
--~ 两个取值false和true。但要注意Lua中所有的值都可以作为条件。在控制结构的条件中除了false和nil为假,其他值都为真。所以Lua认为0和空串都是真。lua是8位字节,所以字符串可以包含任何数值字符,包括嵌入的0.string和其他对象一样,Lua自动进行内存分配和释放.Lua会自动在string和numbers之间自动进行类型转换.可以调用tostring()将数字转成字符串
--~ 标准库包括string库、table库、I/O库、OS库、算术库、debug库.userdata可以将C数据存放在Lua变量中,userdata在Lua中除了赋值和相等比较外没有预定义的操作。userdata用来描述应用程序或者使用C实现的库创建的新类型。例如:用标准I/O库来描述文件
--~ serdata这东西, 可以理解为用户自定义数据. 它是数据, 不是类型.就是一片内存. 通过一个简单的API, 我们就能获取一个userdata:
a = "one string"
b = string.gsub(a, "one", "another")   -- change string parts
print(b)
--~ 页面中的埋点javascript片段会被执行
--~  #此请求不缓存
--~ 	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";
--~  internal;
location /i-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 /path/to/logs/directory/ma.log tick;
	#输出空字符串
	echo '';
}1
作者:AngryFox 分类: Uncategorized June 22nd, 2013 暂无评论

1. Nginx进程模型
Nginx采用多进程模型,单Master—多Worker,由Master处理外部信号、配置文件的读取及Worker的初始化,Worker进程采用单线程、非阻塞的事件模型(Event Loop,事件循环)来实现端口的监听及客户端请求的处理和响应,同时Worker还要处理来自Master的信号。由于Worker使用单线程处理各种事件,所以一定要保证主循环是非阻塞的,否则会大大降低Worker的响应能力。

2. Nginx处理Http请求的过程
表面上看,当Nginx处理一个来自客户端的请求时,先根据请求头的host、ip和port来确定由哪个server处理,确定了server之后,再根据请求的uri找到对应的location,这个请求就由这个location处理。实际Nginx将一个请求的处理划分为若干个不同阶段(phase),这些阶段按照前后顺序依次执行,也就是说NGX_HTTP_POST_READ_PHASE在第一个,NGX_HTTP_LOG_PHASE在最后一个。

NGX_HTTP_POST_READ_PHASE,     //0读取请求phase
NGX_HTTP_SERVER_REWRITE_PHASE,//1这个阶段主要是处理全局的(server block)的rewrite
NGX_HTTP_FIND_CONFIG_PHASE,   //2这个阶段主要是通过uri来查找对应的location,然后根据loc_conf设置r的相应变量
NGX_HTTP_REWRITE_PHASE,       //3这个主要处理location的rewrite
NGX_HTTP_POST_REWRITE_PHASE,  //4postrewrite,这个主要是进行一些校验以及收尾工作,以便于交给后面的模块。
NGX_HTTP_PREACCESS_PHASE,     //5比如流控这种类型的access就放在这个phase,也就是说它主要是进行一些比较粗粒度的access。
NGX_HTTP_ACCESS_PHASE,        //6这个比如存取控制,权限验证就放在这个phase,一般来说处理动作是交给下面的模块做的.这个主要是做一些细粒度的access
NGX_HTTP_POST_ACCESS_PHASE,   //7一般来说当上面的access模块得到access_code之后就会由这个模块根据access_code来进行操作
NGX_HTTP_TRY_FILES_PHASE,     //8try_file模块,就是对应配置文件中的try_files指令,可接收多个路径作为参数,当前一个路径的资源无法找到,则自动查找下一个路径
NGX_HTTP_CONTENT_PHASE,       //9内容处理模块
NGX_HTTP_LOG_PHASE            //10log模块    

每个阶段上可以注册handler,处理请求就是运行每个阶段上注册的handler。Nginx模块提供的配置指令只会一般只会注册并运行在其中的某一个处理阶段。比如,set指令属于rewrite模块的,运行在rewrite阶段,deny和allow运行在access阶段。
3. 子请求(subrequest)
其实在Nginx 世界里有两种类型的“请求”,一种叫做“主请求”(main request),而另一种则叫做“子请求”(subrequest)。
所谓“主请求”,就是由 HTTP 客户端从 Nginx 外部发起的请求。比如,从浏览器访问Nginx就是一个“主请求”。
而“子请求”则是由 Nginx 正在处理的请求在 Nginx 内部发起的一种级联请求。“子请求”在外观上很像 HTTP 请求,但实现上却和 HTTP 协议乃至网络通信一点儿关系都没有。它是 Nginx 内部的一种抽象调用,目的是为了方便用户把“主请求”的任务分解为多个较小粒度的“内部请求”,并发或串行地访问多个 location 接口,然后由这些 location 接口通力协作,共同完成整个“主请求”。当然,“子请求”的概念是相对的,任何一个“子请求”也可以再发起更多的“子子请求”,甚至可以玩递归调用(即自己调用自己)。当一个请求发起一个“子请求”的时候,按照 Nginx 的术语,习惯把前者称为后者的“父请求”(parent request)。
“子请求”方式的通信是在同一个虚拟主机内部进行的,所以 Nginx 核心在实现“子请求”的时候,就只调用了若干个 C 函数,完全不涉及任何网络或者 UNIX 套接字(socket)通信。我们由此可以看出“子请求”的执行效率是极高的。
4. 协程(Coroutine)
协程类似一种多线程,与多线程的区别有:
1. 协程并非os线程,所以创建、切换开销比线程相对要小。
2. 协程与线程一样有自己的栈、局部变量等,但是协程的栈是在用户进程空间模拟的,所以创建、切换开销很小。
3. 多线程程序是多个线程并发执行,也就是说在一瞬间有多个控制流在执行。而协程强调的是一种多个协程间协作的关系,只有当一个协程主动放弃执行权,另一个协程才能获得执行权,所以在某一瞬间,多个协程间只有一个在运行。
4. 由于多个协程时只有一个在运行,所以对于临界区的访问不需要加锁,而多线程的情况则必须加锁。
5. 多线程程序由于有多个控制流,所以程序的行为不可控,而多个协程的执行是由开发者定义的所以是可控的。
Nginx的每个Worker进程都是在epoll或kqueue这样的事件模型之上,封装成协程,每个请求都有一个协程进行处理。这正好与Lua内建协程的模型是一致的,所以即使ngx_lua需要执行Lua,相对C有一定的开销,但依然能保证高并发能力。
5. ngx_lua原理
ngx_lua将Lua嵌入Nginx,可以让Nginx执行Lua脚本,并且高并发、非阻塞的处理各种请求。Lua内建协程,这样就可以很好的将异步回调转换成顺序调用的形式。ngx_lua在Lua中进行的IO操作都会委托给Nginx的事件模型,从而实现非阻塞调用。开发者可以采用串行的方式编写程序,ngx_lua会自动的在进行阻塞的IO操作时中断,保存上下文;然后将IO操作委托给Nginx事件处理机制,在IO操作完成后,ngx_lua会恢复上下文,程序继续执行,这些操作都是对用户程序透明的。
每个NginxWorker进程持有一个Lua解释器或者LuaJIT实例,被这个Worker处理的所有请求共享这个实例。每个请求的Context会被Lua轻量级的协程分割,从而保证各个请求是独立的。
ngx_lua采用“one-coroutine-per-request”的处理模型,对于每个用户请求,ngx_lua会唤醒一个协程用于执行用户代码处理请求,当请求处理完成这个协程会被销毁。每个协程都有一个独立的全局环境(变量空间),继承于全局共享的、只读的“comman data”。所以,被用户代码注入全局空间的任何变量都不会影响其他请求的处理,并且这些变量在请求处理完成后会被释放,这样就保证所有的用户代码都运行在一个“sandbox”(沙箱),这个沙箱与请求具有相同的生命周期。
得益于Lua协程的支持,ngx_lua在处理10000个并发请求时只需要很少的内存。根据测试,ngx_lua处理每个请求只需要2KB的内存,如果使用LuaJIT则会更少。所以ngx_lua非常适合用于实现可扩展的、高并发的服务。
6. 典型应用
官网上列出:
· Mashup’ing and processing outputs of various nginx upstream outputs(proxy, drizzle, postgres, redis, memcached, and etc) in Lua,
· doing arbitrarily complex access control and security checks in Luabefore requests actually reach the upstream backends,
· manipulating response headers in an arbitrary way (by Lua)
· fetching backend information from external storage backends (likeredis, memcached, mysql, postgresql) and use that information to choose whichupstream backend to access on-the-fly,
· coding up arbitrarily complex web applications in a content handlerusing synchronous but still non-blocking access to the database backends andother storage,
· doing very complex URL dispatch in Lua at rewrite phase,
· using Lua to implement advanced caching mechanism for nginxsubrequests and arbitrary locations.

7. Connection Pool
前面访问redis和memcached的例子中,在每次处理一个请求时,都会和后端的server建立连接,然后在请求处理完之后这个连接就会被释放。这个过程中,会有3次握手、timewait等一些开销,这对于高并发的应用是不可容忍的。这里引入connection pool来消除这个开销。
连接池需要HttpUpstreamKeepaliveModule模块的支持。
配置:
http {
# 需要HttpUpstreamKeepaliveModule
upstream redis_pool {
server 127.0.0.1:6379;
# 可以容纳1024个连接的连接池
keepalive 1024 single;
}

server {
location = /redis {

redis2_pass redis_pool;
}
}
}
这个模块提供keepalive指令,它的context是upstream。我们知道upstream在使用Nginx做反向代理时使用,实际upstream是指“上游”,这个“上游”可以是redis、memcached或是mysql等一些server。upstream可以定义一个虚拟server集群,并且这些后端的server可以享受负载均衡。keepalive 1024就是定义连接池的大小,当连接数超过这个大小后,后续的连接自动退化为短连接。连接池的使用很简单,直接替换掉原来的ip和端口号即可。
有人曾经测过,在没有使用连接池的情况下,访问memcached(使用之前的Memc模块),rps为20000。在使用连接池之后,rps一路飙到140000。在实际情况下,这么大的提升可能达不到,但是基本上100-200%的提高还是可以的。
5. 小结
这里对memcached、redis的访问做个小结。
1. Nginx提供了强大的编程模型,location相当于函数,子请求相当于函数调用,并且location还可以向自己发送子请求,这样构成一个递归的模型,所以采用这种模型实现复杂的业务逻辑。
2. Nginx的IO操作必须是非阻塞的,如果Nginx在那阻着,则会大大降低Nginx的性能。所以在Lua中必须通过ngx.location.capture发出子请求将这些IO操作委托给Nginx的事件模型。
3. 在需要使用tcp连接时,尽量使用连接池。这样可以消除大量的建立、释放连接的开销。

参考:

http://wiki.nginx.org/HttpUpstreamKeepaliveModule

http://wiki.nginx.org/HttpRedis2Module

http://wiki.nginx.org/HttpMemcModule

作者:AngryFox 分类: Uncategorized June 22nd, 2013 暂无评论
1. post方法请求静态文件
默认情况下,web服务器都不允许post方法请求静态文件,会返回响应403 Not Allowed。但是有些时候确实有这种需求。可以通过配置文件来改变这种设置:在需要处理静态文件的location里这样配置即可,
location /static/ {
    root /path/to/files/;
    error_page 405 =200 $uri;
}
可以解析html到.sock接收post数据
2. Nginx默认一次只能发送50个子请求(subrequest)
在nginx源码中,src/http/ngx_http_request.h文件中:
#define NGX_HTTP_MAX_SUBREQUESTS        50
在使用Openresty时,可以向configure脚本传参设置这个限制, ./configure --with-cc-opt="-D NGX_HTTP_MAX_SUBREQUESTS=250"
3. Nginx location匹配规则
匹配顺序:
a. 字符串匹配,和location块的顺序无关,根据uri匹配所有的location,从而得到一个匹配度最大的location。
b. 正则匹配,按照location块的顺序从前向后,如果找到匹配的location,则直接由该location处理请求。如果所有的location都不匹配,则由在字符串匹配中,匹配度最大的location处理。
匹配规则:
= /uri/   ——字符串精确匹配
^~ /uri/ ——字符串前缀匹配
~ /uri/   ——大小写区分的正则匹配
~* /uri/ ——大小写不区分的正则匹配
@ /uri/ ——命名location,只用于内部重定向请求
其中,如果=和^~匹配成功之后会立即停止搜索,即不再进行正则匹配。
4. 监控Nginx的状态
需要HttpStubStatusModule模块,默认情况是不开启的,所以需要编译时,指定开启这个模块。
./configure --with-http_stub_status_modules
nginx的配置:
location /nginx_status {
  # copied from http://blog.kovyrin.net/2006/04/29/monitoring-nginx-with-rrdtool/
  stub_status on;
  access_log   off;
  allow SOME.IP.ADD.RESS;
  deny all;
}
然后通过浏览器访问localhost/nginx_status,浏览器显示Nginx的状态
Active connections: 291
server accepts handled requests
  16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
5. Nginx启用aio
默认Nginx是没有开启aio的,需要在配置编译时,加上相应选项否则启动Nginx会报错unknown directive “aio”。
./configure --with-file-aio
6. 限制请求内容的大小
指令:client_max_body_size,用于设置这个值,默认是1m。context可以是http,server或者location。
7. 通过echo模块合并静态文件请求
正常html中包含多个js文件或者css文件,那么浏览器需要多次http请求才能完成这些文件的加载。比如html文件:
<html>
<head>
    <script type='text/javascript' src='/static/a.js'></script>
    <script type='text/javascript' src='/static/b.js'></script>
    <script type='text/javascript' src='/static/c.js'></script>
……
</html>
那么就需要3次请求。下面介绍echo模块实现请求合并。先修改html:
<html>
<head>
    <script type='text/javascript' src='/merge?/static/a.js&/static/b.js&/static/c.js'></script>
……
</html>
nginx配置文件:
1 view plaincopyprint?
location /static/ {
    root /home/www/doc_root;
}  

location /merge {
    default_type 'text/javascript';  

    echo_foreach_split '&' $query_string;    # 将查询字符串以&分割
        echo_location_async $echo_it;        # 发送子请求到$echo_it对应的location
        echo;
    echo_end;
}
通过这种方式可以有效减少客户端请求,降低服务器端的压力。
8. nginx开启gzip
gzip    on;    # 开启gzip,默认关闭
gzip_comp_level    5;    # 压缩级别,1-9,级别越高压缩率越高,但是相应的耗cpu
gzip_min_length    1025;    # 当响应内容大小大于多少bytes后使用gzip
gzip_types    text/plain application/x-javascript application/json text/javascript text/css    # 对于什么类型的内容使用gzip 
作者:AngryFox 分类: Uncategorized June 22nd, 2013 暂无评论
1. 单个进程打开的文件描述符最大值
默认是1024,可以通过ulimit -a查看所有的限制
ulimit -n 可以查看文件描述符最大值。
并可以通过ulimit -n 65535设置这一次会话的最大值。
2. 查看linux机器的连接数
netstat -est | grep established | grep -v packets
    1186 connections established
3. 生成动态链接库
gcc -fpic -shared -o mylib.so mylib.c
-fpic:输出的对象是地址可重定位的。
-shared:指定生成动态链接库。
使用动态链接库:
gcc -o libtst libtst.c /home/tst/mylib.so
或者吧mylib.so拷贝到/usr/lib/下,然后使用:
gcc -o libtst libtst.c -lmylib
4. 生成静态链接库
1)用gcc编绎该文件,可以使用任何合法的编绎参数
gcc -c lib_test.c -o lib_test.o
2) $ar crv libtest.a lib_test.o   //生成静态库生成libtest.a
3) 在某些系统中还要为静态库生成一个内容表   $ranlib libtest.a
4) 使用静态链接库
        $nm libtest.a    //nm工具可以打印出库中的涉及到的所有符号,库既可以是静态的也可以是动态的。nm列出的符号有很多, 常见的有三种,一种是在库中被
                             调用,但并没有在库中定义(表明需要其他库支持),用U表示;一种是库中定义的函数,用T表示,这是最常见的;另外一种是所谓的"弱态”
                             符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。
$gcc -c -I/home/lib main.c //假设main.c要使用对应的静态库
$gcc -o main -L/home/lib main.o libtest.a
说明:这里的-I/home/lib和-L/home/lib是通过-I和-L指定对应的头文件和库文件的路径,libtest.a就是要用的静态库。在main.c中要包含静态库的头文件。
5. 彻底删除文件
一般,我们都是用rm来删除文件或文件夹,但是rm并不真的清空该文件所占用的数据块的内容,而只是释放了索引节点,所以可以通过一些恢复这些文件。有时,需要彻底删除文件,以释放空间,可以使用shred命令。
shred -u file_path
运行这个命令就可以彻底删除文件。
经过测试,shred这个命令在运行时很耗cpu。实际上,用rm就可以达到彻底删除文件,并释放空间的目的:
rm -rf file_path
这个命令就相当于windows下shift+delete。
还可以使用
> file_path
这样可以清空文件的内容

6. 查看CPU每个核的信息
使用top,然后按1,显示所有核的状态:
top - 00:18:57 up  1:17,  6 users,  load average: 0.48, 0.75, 1.36
Tasks: 200 total,   1 running, 199 sleeping,   0 stopped,   0 zombie
Cpu0  : 27.8%us,  2.7%sy,  0.0%ni, 60.2%id,  9.4%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  : 25.3%us,  3.7%sy,  0.0%ni, 71.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  : 28.7%us,  4.0%sy,  0.0%ni, 67.3%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  : 26.3%us,  2.7%sy,  0.0%ni, 71.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
7. 定时执行命令,观察输出
如果要持续执行一个命令,然后观察输出的话,我们一遍一遍的按上回车,然后看着输出。watch命令可以很好的实现这个功能。比如,要监控linux的连接数:
watch -n 2 -d 'netstat -est | grep est'
输出:
Every 2.0s: netstat -est | grep est                                                                                              Mon Mar 19 10:46:03 2012  

    12784 connections established
    556 packets rejects in established connections because of timestamp
    2015 congestion windows fully recovered without slow start
    9672 congestion windows partially recovered using Hoe heuristic
    20983 congestion windows recovered without slow start by DSACK
    22010 congestion windows recovered without slow start after partial ack
-n指定命令执行的时间间隔,-d高亮显示连续两侧输出的差别,为了便于观察,最后一个参数是要执行的命令。
8. ubuntu下配置ssh服务
安装openssh-server:
1 view plaincopyprint?
apt-get install openssh-server
启动命令:
1. service ssh start
2. /etc/init.d/ssh start
第一种是推荐的方式。
9. 查看cpu和内核信息
a. 查看os内核信息
uname -a
Linux vyz20242 2.6.32-5-xen-amd64 #1 SMP Tue Jun 14 12:46:30 UTC 2011 x86_64 GNU/Linux
b. 操作系统发行版本信息
cat /etc/issue | grep Linux
Debian GNU/Linux 6.0 \n \l
c. linux内核版本
cat /proc/version
Linux version 2.6.32-5-amd64 (Debian 2.6.32-41squeeze2) (dannf@debian.org) (gcc version 4.3.5 (Debian 4.3.5-4) ) #1 SMP Thu Mar 22 17:26:33 UTC 2012
10. 查看系统页大小
getconf PAGESIZE  
作者:AngryFox 分类: Uncategorized June 22nd, 2013 暂无评论

linux性能监控,就是要监控系统的各个子系统是否正常。linux主要的子系统包括:CPU、Memory、IO和Network,它们之间相互依赖,一个出问题可能会影响其他的。比如:网卡流量很大会导致更多的CPU开销,因为频繁的响应中断执行协议栈。
性能监控时,首先要确定应用的类型,然后对症下药,可以将应用分成:
CPU密集型:CPU开销很高,比如大量的CPU运算、科学计算等。通常web server属于这类。
IO密集型:大量的磁盘读写,高负荷的内存使用。IO密集型不会对CPU发起更多的请求,它占用CPU只是为了产生IO请求然后sleep。通常数据库属于这一类型。
1. CPU
1. 上下文切换
上下文切换(Context Switch)是多任务操作系统的基石,每个进程都有自己的执行环境就是进程的上下文,这些上下文就是当前进程使用的寄存器,比如PC、栈指针%esp、帧指针%ebp等。每个处理器核心在任意时刻只能执行一个进程或线程,当线程的时间片用尽或者线程阻塞(比如线程执行磁盘io或网络io时),进程调度器会根据一定的规则(比如选择运行时间加权之后最少的)挑选一个线程占有处理器。因为CPU的寄存器集合只有一份,所有必须要保存上一个进程使用的寄存器,同时恢复被调度的进程的寄存器状态。
上下文切换时有损耗的。一般地,当系统中线程过多或者io负载过高时,上下文切换会比较频繁,此时CPU时间过多的消耗在上下文切换。
2. 运行队列
内核中的进程调度器维护着运行队列(run queue),而每个阻塞源维护着自己的阻塞队列(wait queue,比如一个文件描述符,正在读写该文件的进程会被阻塞在其阻塞队列上)。任意一个进程要么处于可执行状态,要么阻塞状态。所有的可执行状态的进程都处于运行队列,如果当前系统CPU负载很高,那么运行队列的长度很大,进程调度器就不会及时响应系统请求。当运行队列越来越大时,进程或线程将花费更多的时间来获取CPU时间。
> cat /proc/loadavg
上述命令可以显示系统负载,分别是:过去1分钟、5分钟、15分钟的系统负载、当前正在执行的进程数/系统的所有进程数、最后执行的进程的pid。top命令也可以现在系统负载。
3. vmstat
vmstat提供一种低开销的方式统计系统性能数据。
> vmstat 1 1
procs ———–memory——————- —swap– —–io—- -system– —-cpu—-
r b swpd free buff cache si so bi bo in cs us sy id wa
与CPU相关的各个列的意思:
r:当前运行队列中的进程的数目,就是那些处于可执行状态,但是得不到CPU的进程。
b:当前处于阻塞状态,并等待IO请求完成的进程的数目。
in:当前处理的中断数目。
cs:当前系统发生的上下文切换次数。
us:CPU在用户空间执行的时间的百分比。
sy:CPU在内核空间执行的时间的百分比。
id:CPU空闲时间的百分比。
wa:由于所有可运行进程等待IO请求完成被阻塞导致的CPU空闲时间的百分比。
4. pidstat
pidstat用于查看进程所属的线程的CPU的使用情况。
> pidstat -p 2036 -t 1 1
Linux 2.6.32-5-xen-amd64 (vzw51173.puppetclient.163.com) 2012年10月12日 _x86_64_ (4 CPU)
16时09分17秒 TGID TID %usr %system %guest %CPU CPU Command

Linux中是不区分进程和线程的,线程就是可以共享资源的进程。TID就是线程的PID,这里可以查看各个线程的CPU使用情况。
5. 总结
vmstat -> top -> pidstat
通过vmstat查看整体CPU使用情况,top查看CPU占用高的几个进程,pidstat查看这几个进程对应的线程。
监控CPU性能包括以下几个部分:
a. 检查CPU的run queue,每个CPU的run queue最好不要超过3个进程。
b. 确定CPU利用率在usr/sys = 65% / 35% ~ 70% / 30%之间。
c. 当CPU的处理时间更多的是在system空间,说明已经超负荷。
d. 当I/O增多时,CPU密集型的应用将受到影响。
e. 当CPU的IOWait占用比较大的比例时,说明IO出现异常。
2. Memory
1. 虚拟内存
虚拟内存就是在硬盘上划出一部分区域做为内存使用,当系统可用的内存低于某个值时,内核就会将当前不再活跃的内存块写入磁盘,然后这块内存可以作为其他用途使用。当cpu需要访问被写入磁盘的数据时,再把它读入到内存中。上述操作对用户来说是透明的,磁盘的读写是很慢的,比内存要慢几千万倍(磁盘10ms,内存100-200ns),所以要尽量把数据放在内存中,程序才会运行的更快。硬盘中用作替代内存的部分就是虚拟内存,成为swap space。
2. 页高速缓存
Linux内核通过页高速缓存来加快磁盘操作。当系统存在大量的空闲内存时,内核会把空闲内存的一部分做为页高速缓存。当某个磁盘块被缓存之后,对于这个磁盘块的读写就相当于内存操作。读操作时直接读对应的page,写操作时将内容写入对应的page,然后将该page标记为脏页,会有后台线程flush(linux 2.6内核)完成将脏页同步回磁盘。flush在脏页比例大于某个阈值,或者修改超过一定时间之后进行写回。

[/plain] view plaincopyprint?
> ps axu | grep flush
root       927  0.0  0.0      0     0 ?        S    May23   2:34 [flush-202:2]
root     14413  0.0  0.0  10200   856 pts/2    S+   16:53   0:00 grep flush
3. vmstat
        vmstat可以查看系统的内存相关信息。
1 view plaincopyprint?
> pidstat -r -p 31679 1 1
Linux 2.6.32-5-xen-amd64 (debian-org)   2012年10月12日  _x86_64_        (4 CPU)  

17时10分02秒       PID      minflt/s  majflt/s     VSZ    RSS    %MEM  Command
        各个列含义:
        minflt/s:进程平均每s造成的minor fault,这些错误不会导致从磁盘加载内存页。
        majflt/s:进程平均每s造成的major fault,这些错误会导致从磁盘加载内存页。
        VSZ:进程使用的所有虚拟内存的大小(KB)。
        RSS:进程使用的物理内存大小(KB)。
        %MEM:占用物理内存百分比。
5. sar
        a. 查看页统计信息
1 view plaincopyprint?
> sar -r
Linux 2.6.32-5-xen-amd64 (debian-org)   2012年11月17日  _x86_64_        (4 CPU)  

00时00分01秒 kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  

10时15分01秒 kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit
        各个列的含义:
        kbmemfree:可用的空闲内存(KB)。
        kbmemused:使用的内存,不包括内核自己使用的内存(KB)。
        %memused:使用的内存的比例。
        kbbuffers:被内核用做缓冲区的内存(KB)。
        kbcached:被内核用来缓存数据的内存(KB)。
        kbcommit:对于当前的工作量需要的内存(KB),确定RAM/Swap的大小以防止out of memory。
        %commit:当前的工作量需要的内存和所有内存(RAM+Swap)的百分比。
        上面两种sar的使用方式可以查看从0点到现在的每分钟的统计信息,可以通过sar -B interval times以固定间隔时间interval秒统计times次数据,比如:
1

view plaincopyprint?
sar -B 1 5
Linux 2.6.32-5-xen-amd64 (debian-org) 2012年11月17日 _x86_64_ (4 CPU)

18时53分20秒 pgpgin/s pgpgout/s fault/s majflt/s pgfree/s pgscank/s pgscand/s pgsteal/s %vmeff
5. 总结
a. 当系统出现较少的page fault,说明页命中率很高,会获得较好的响应时间。
b. 在没有写入swap和disk的情况下,空闲内存越小,表明页高速缓存利用率越高。
c. 如果系统不断报告swap device繁忙,那么说明系统内存不足。

作者:AngryFox 分类: Uncategorized June 22nd, 2013 暂无评论

inetd是监视一些网络请求的守护进程,其根据网络请求来调用相应的服务进程来处理连接请求。它可以为多种服务管理连接,当 inetd 接到连接时,它能够确定连接所需的程序,启动相应的进程,并把 socket 交给它 (服务 socket 会作为程序的标准输入、 输出和错误输出描述符)。 使用 inetd 来运行那些负载不重的服务有助于降低系统负载,因为它不需要为每个服务都启动独立的服务程序。
一般说来, inetd 主要用于启动其它服务程序,但它也有能力直接处理某些简单的服务, 例如 chargen、 auth, 以及 daytime。
inetd 是通过rc系统启动的。 inetd_enable 选项默认设为 NO,但可以在安装系统时, 由用户根据需要通过 sysinstall 来打开。
inetd.conf则是inetd的配置文件。 inetd.conf文件告诉inetd监听哪些网络端口,为每个端口启动哪个服务。在任何的网络环境中使用Linux系统,第一件要做的事就是了解一下服务器到底要提供哪些服务。不需要的那些服务应该被禁止掉,最好卸载掉,这样黑客就少了一些攻击系统的机会。查看“/etc/inetd.conf”文件,了解一下inetd提供哪些服务。用加上注释的方法(在一行的开头加上#号),禁止任何不需要的服务,再给inetd进程发一个SIGHUP信号。
inetd提供一种简单的方法是应用程序具有网络功能。
对于TCP服务器,inetd监听在应用程序已知的端口上,监听链接请求,接受连接,映射链接到标准输入,标准输出和标准错误输出,启动适当的服务器。
对于UDP服务器,当UDP服务器的已知端口上数据可读时)inetd要求操作系统通知他,知道inetd启动的服务器中止,inetd再在已知端口上进行下一步操作。