grep 零宽断言

2019-03-02 23:41|来源: 网路


 

算是正则环视的一个简单应用吧。

http://pengqi.me/2011/05/%E4%BD%BF%E7%94%A8grep%E5%92%8C%E6%AD%A3%E5%88%99%E6%9D%A5%E5%88%86%E6%9E%90web%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%97%A5%E5%BF%97/

前两天因为第三方游戏服务器掉线,导致大量用户同时登录我的服务器,将服务器负载瞬间提高到200+,如此恐怖的数字让我不得不考虑增加服务器来抵抗问题重现,然而我的服务器平时负载都很低,0.1都不到,增加服务器来应付这样短暂的风暴未免太过于浪费,于是我决定从日志下手,找到我的网站的瓶颈,希望能通过改善程序来解决这个问题。

第一步,定位时间

我的日志文件里包含了最近一个礼拜的数据,然而我需要的只是风暴发生时产生的数据,总共不超过20分钟,怎么取呢?因为我的Web服务器采用的是标准的combined格式

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

而且全部是PHP动态请求,所以我决定从time_local下手,找到并发访问量最高的时间段,这很容易办到:

grep -oP '12\/May\/2011(:\d{2}){3}' access.log | uniq -c | sort -n > time.sort

得到如下结果(部分)

从18点43分开始,我的服务器每秒需要响应120多次动态请求,而18:46:49秒更加变态,211次!看来把我服务器拖垮的,就是18点43分到18点50分这一段。

第二步, 过滤脚本

定位好了时间,现在要做的,就是取出这一时间段的日志再做分析,使用以下脚本将18:43分到18:50分之间的日志取出来

grep -P '12\/May\/2011:18:4[3-9]:\d{2}' access.log > storm.log第三步, 找出元凶

得到了storm.log,下面我便要找出拖垮我服务器的元凶,及访问数量最高的$request。因为$request都是 “GET /***** HTTP/1.1″ 或 “POST /****” 这样的格式, 所以可以通过简单的正则取出$request,如下

grep -oP '((?<=GET\s)|(?<=POST\s))[^?\s]+' storm.log | sort \ | uniq -c | sort -n > request.sort

(?<=GET\s)这个正则叫作零宽断言,是将要被匹配文字前面的条件,即除非前面有’GET ‘出现,后面的才匹配。
上面的脚本得到以下结果:

从这张图我们便可以找出程序方面的瓶颈了,因为上面这些请求大部分都是ajax请求,所以明显的,像’users/getuser’、’games/getservers/sxd’这样的数据请求完全可以被浏览器缓存起来,而’users/logoutService’根据我们的业务逻辑也显得毫无必要,将这三项请求砍掉能节省将近60%的资源!
以下便是通过优化程序代码后13号应对的又一次风暴结果。

看到没,’/users/getuser’请求减少了将近一半,而’/games/getserver/sxd’则减少了近75%,总量减少了近40%!然而’/users/logoutService’却不尽如人意,只少了三成,我们待会再寻找其原因。

通过以上程序的优化和一些系统配置的调整,这次风暴只将我的服务器负载升高到了10+,并在十几秒后就很快地平稳了下来,和前一天的200+相比,可以说成功地解决了短暂风暴的问题。

第四步,找出来源

上一步遗留了一个问题,即我明明优化了程序,去掉了不必要的’users/logoutService’,为何在风暴中,它依然出现了那么多次,所以我决定分析$http_referer,找出这些请求都是从哪来的。
根据日志的格式,$http_referer前面都有一个$body_bytes_sent、一个空格和一个双引号,例如

27.37.113.145 - - [14/May/2011:22:30:47 +0800] "GET /users/logoutService HTTP/1.1" 200 431 "http://sxd.xd.com/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"

因为$body_bytes_sent始终是个数字,这样就可以通过以下正则来找出来源

grep -P '\/users\/logoutService?' storm.log \ | grep -oP '(?<=\d\s")[^?"]+' | sort | uniq -c | sort -n > logout.referer.sort

先过滤出logout的日志,再通过零宽断言找出来源。得到的结果出乎我意料,来自于一个我在上一步中已经调整过的页面,明明去掉了不必要的’users/logoutService’请求,为何还会重复出现?仔细观察代码后并没发现可疑的地方,于是推测是CDN缓存的问题,这些用户用的JS版本可能还是前一天的,清理缓存,加上版本号,期待下次风暴来验证这一推论!


转自:http://www.cnblogs.com/wangkangluo1/archive/2012/06/20/2556339

相关问答

更多
  • 如果你想做最少的工作,改变 grep -o -P 'PATTERN' file.txt 至 perl -nle 'print $& if m{PATTERN}' file.txt 所以你得到: var1=`perl -nle 'print $& if m{(?<=).*(?=)}' file.txt` var2=`perl -nle 'print $& if m{(property:)\K.*\d+(?=end)}' file.txt` 但是,您可以通过额外的工 ...
  • 当我尝试时,我得到了 $: grep WAT file.txt Binary file file.txt matches grep假设它是一个二进制文件。 添加-a -a, - text等效于--binary-files = text $: grep -a WAT file.txt|head -3 ATOM 29305 O WAT 4060 -75.787 -79.125 25.925 1.00 0.00 O ATOM 29306 H1 WAT 4060 ...
  • 编辑执行此操作: Find: ^(\d+\.\d+\.\d+\.)x Replace: \010 从手册: 如果反斜杠后面有两个以上的十进制数字,则只有前两个数字被视为反向引用的一部分。 因此,“\ 111”将被解释为第11个反向引用,后面是文字“1”。 你可以使用前导零; 例如,如果在替换模式中,您希望第一个反向引用后跟文字“1”,则可以使用“\ 011”。 (如果使用“\ _11”,即使它是空的,也会得到第11个反向引用。) 好吧,如果这是你想要使用的实际字符串,那么有一个简单的解决方案:只需将最后 ...
  • 试图记住多少正则表达式语言grep支持... grep '^....$' words 为你工作? 请注意,由于您正在搜索字典文件,我不确定您需要将自己限制为字母。 Trying to remember how much of regex language grep supports... does grep '^....$' words work for you? Note that since you are searching a dictionary file, I'm not sure you ...
  • 您可以使用dirname可靠地获取目录路径。 folderlist=($(find . -size 0 -print0|xargs -0 -I% dirname %)) for i in "${folderlist[@]}" do echo $i done You can used dirname to reliably get the directory path. folderlist=($(find . -size 0 -print0|xargs -0 -I% dirname %)) for ...
  • ls * | sort -n | xargs -d '\n' grep hello ls * | sort -n | xargs -d '\n' grep hello
  • ? 不是grep支持的基本正则表达式的一部分。 GNU grep支持它们作为扩展,但你必须逃避它们: $ grep '^\[\?MyTag\]\?' file.txt [MyTag] hello MyTag world 或者,正如指出的那样,使用grep -E来启用扩展正则表达式。 对于GNU grep, grep和grep -E之间的唯一区别,即使用基本和扩展正则表达式,是你必须逃避的,不是什么。 基本正则表达式 必须转义捕获组和量化: \( \)和\{ \} 零或一( ? ),一个或多个( + )和交 ...
  • cURL不适合这项工作: lynx -dump -listonly -nonumbers https://www.youtube.com/user/HowdiniGuru/videos | grep watch 例 cURL is not the right tool for this job: lynx -dump -listonly -nonumbers https://www.youtube.com/user/HowdiniGuru/videos | grep watch Example
  • 尝试 grep -oP 'mem\s*=\s*\K[0-9]+' file \K只会丢弃到目前为止匹配的所有内容,只打印数字。 这种替代后面的断言(例如(?<=mem=) )绕过了Tomalak提到的后者的限制: 后置断言必须是固定长度的。 注意:非标准-P选项 - 对于PCRE(Perl兼容正则表达式)支持 - 需要GNU grep 。 Try grep -oP 'mem\s*=\s*\K[0-9]+' file The \K simply drops everything matched so fa ...
  • 将第一级数组grep转换为@result my @result = grep { grep { /search/ } @$_ } @array; 将最终字符串写入@result , my @result = grep { /search/ } map { @$_ } @array; To grep first level of arrays into @result my @result = grep { grep { /search/ } @$_ } @array; grepping final s ...