• 企业命令linux下处理实时日志生成另一个实时日志


    时间紧张,先记一笔,后续优化与完善。

        

    一.背景分析

        

    1.知识点

        写这篇blog,主要有下面几个知识点想分析:

        curl获得http响应内容;

        shell中执行php文件;

        php中执行shell命令(通过exec函数);

        php实现tail -f命令;

        包含空格的参数如何作为参数传递(用双引号括起来)。

        

    2.业务流程

        这篇blog的背景是读取"/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current"这个实时日记,生成招聘会所需的实时日记。

        业务流程如下:

        (1)从http://bj.baidu.com/jobfairs/jobfairs_im_port.php?action=getIms获得企业和IM客户端id的关系。

        响应的格式如下:

        {"status":1,"ret":{"company_id":{“im_accout”:[im_id],"company_name":[]}}}

        获得到的数据如下:

        {"status":1,"ret":{"2028107":{"im_account":["31669394","50000098"],"name":["baidu"]},"2028098":{"im_account":["50029298","50000098","31669376","31669394","50006271"],"name":["sogou"]}},"msg":""}

        这里遇到的第一个问题是我开辟所在的环境和http://bj.baidu.com不在同一个网段内,该url服务所在的IP为10.3.255.201,此时我须要进行hosts映射,这样当我拜访http://bj.baidu.com/jobfairs/jobfairs_im_port.php?action=getIms时,便相称于我在拜访了http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms。

        但是我们一定有一个疑问,为什么我们不直接使用http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms来进行拜访,谜底是我们须要通过url获得到用户的城市,即http://bj.baidu.com/jobfairs/jobfairs_im_port.php?action=getIms,这里面包含bj.baidu.com,包含用户的城市信息bj。

        解决方法是通过curl对url和host进行映射:

        curl -H "Host: bj.ganji.com" http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms

        参考链接:http://blog.csdn.net/lianxiang_biancheng/article/details/7575370  中的curl命令的使用。

        (2)其次,如果这条日记记录中fromUserId或者toUserId包含某个企业的IM客户端id,则说明这条消息属于这个企业;

        (3)最后,生成所需格式的日记,日记的字段格式如下:
    时间 企业Id 企业名称 企业IM的id  应聘者IM的id 谁发送的信息(0:企业,1:应聘者)  消息内容

        

    二.采取了三种实现方法

        

    1.第一种:shell读取每一行记录传递给php进行匹配并输出

        (1)start.sh是启动文件,如下:

    #!/bin/sh
    
    #执行前清除所有该进程
    pids=`ps aux | grep jobfairs | grep -v "grep" | awk '{print $2}'`      
    if [ "$pids" != "" ];then 
        echo $pids
        kill -9 $pids
    fi
    sh jobfairs.sh >> /home/baidu/log/jobfairs.log

        (2)jobfairs.sh是获得http内容,读取实时日记并每2分钟重新请求的实现,如下:

    #!/bin/sh
    
    logfile="/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current"
    
    hours=`date +%H`
    start_time=`date +%s`
    
    #17点后停止运行程序
    while [ $hours -lt 17 ]
    do
        res=`curl -s -H "Host: bj.baidu.com" http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms`
        #echo $res
        
        len=${#res}
        if [ $len = 0 ]; then
            echo "Failed! Request error!"
            exit
        fi
    
        status=`echo $res | sed -e 's/.*status"://' -e 's/,.*//'`
        if [ $status != 1 ]; then 
            echo "Failed! Request stauts:"$status
            exit
        fi
    
        ret=`echo $res | sed -e 's/.*ret"://' -e 's/,"msg.*//'`
        #ret='{"2028097":{"im_account":["2875001357","197823104","3032631861","197305863"],"name":["8\u811a\u732b\u521b\u65b0\u79d1\u6280\u6709\u9650\u516c\u53f8\uff08\u4e60\u5927\u7237\u6dae\u8089\u5bf9\u976210000\u7c73\u7684\u79d1\u6280\u516c\u53f8\uff09"]},"2028098":{"im_account":["3658247660","192683241","197488883","108963206","197305001"],"name":["9\u811a\u732b\u521b\u65b0\u79d1\u6280\u6709\u9650\u516c\u53f8"]}}';
    
        tail -f $logfile | grep sendMsgOk | grep "spamReasons=\[\]" | awk -F"\t" '{
            printf("%s\t%s\t%s\t%s\n",$1,$3,$4,$11); 
        }' | while read line
        do
            /usr/local/webserver/php/bin/php jobfairs.php $ret "$line"
            
            #120s后停止生成日记,重新执行http请求去获得公司相干信息
            end_time=`date +%s`
            if [ $(expr $end_time - $start_time) -ge 120 ]; then
                #echo `date +%T`" "`date +%D`
                #echo "120s is done!"
                break
            fi
        done
        
        start_time=`date +%s`
        hours=`date +%H`
    done

        这里还涉及到一个知识点,就是如何将包含空格的字符串作为参数传递。

        这里的场景是这样的:由于一行记录各个字段是以制表符分隔的,其中有一个字段msgContent是消息内容,而消息中经常包含空格,而php接受外来参数默许是以空格分隔的,这样如果将$line作为参数进行传递,就致使msgContent被分隔为了好几个字段。那我们如何解决这个问题呢,谜底就是通过加双引号(即将$line变为"$line"),将一行记录作为一个团体字符串传入即可,然后php接收到这个字符串后,再通过explode("\t",$line)进行分隔出各个字段。如下所示:        

        /usr/local/webserver/php/bin/php jobfairs.php $ret

        

        "$line" 

        

        (3)jobfairs.php是对实时日记的每一行进行匹配并输出为IM的log格式:

    <?php
        $ret = $_SERVER["argv"][1];
        $arr = json_decode($ret, true);//将json字符串解码成数组
        foreach ($arr as $key => $value) {
            $name = $value["name"][0];//企业名称
            foreach ($value["im_account"] as $v) { //企业对应的叮咚id
                $userId[$v] = $key;
                $compName[$v] = $name;
                //echo $key ."\t" . $v ."\t" . $name ."\n";
            }
        }
      
        $line = $_SERVER["argv"][2];//获得日记的一条记录
        $logArr = explode("\t", $line);
        //echo $line . "\n";
    
        //获得各个字段
        $time = $logArr[0];
        $fromUserId = $logArr[1];
        $toUserId = $logArr[2];
        $msgContent = $logArr[3];
        
        $fuiArr = explode('=', $fromUserId);
        $tuiArr = explode('=', $toUserId);
        $fui = $fuiArr[1];
        $tui = $tuiArr[1];
    
        $output = $time . "\t";
        if(isset($userId[$fui])) { //fromUserId是某个企业的叮咚id
            //echo $line . "\n";
            $output .= "companyId=$userId[$fui]\t";
            $output .= "companyName=$compName[$fui]\t";
            $output .= "companyDingdongId=$fui\t";
            $output .= "personalDingdongId=$tui\t";
            $output .= "whoSend=0\t";
            $output .= $msgContent;
            echo $output . "\n";
        } else if(isset($userId[$tui])) { //toUserId是某个企业的叮咚id
            //echo $line . "\n";
            $output .= "companyId=$userId[$tui]\t";
            $output .= "companyName=$compName[$tui]\t";
            $output .= "companyDingdongId=$tui\t";
            $output .= "personalDingdongId=$fui\t";
            $output .= "whoSend=1\t";
            $output .= $msgContent;
            echo $output . "\n";
        }
    ?>

        

    2.第二种:php执行shell命令并将输出结果进行匹配

        注:该方法不能生成实时日记,因为tail -f命令是实时获得更新命令,php无法获得其返回结果。所以该方法仅用于读取一段牢固文本并进行处理。

        这里通过exec执行tail -n1000这个shell命令,获得到最后1000行数据然后再进行处理。同时,这里还调用了php中curl模块获得http响应内容。该文件名为jobfairs2.php。

        每日一道理
    灯,带有一种明亮的光,每当深夜来临,是它陪伴着你,如此默默无闻。它是平凡的,外表华丽与否,那都是一样的,珍珠点缀,水晶加饰的灯它只能用以装饰,来满足人们的虚荣心,比起这,普普通通的日光灯是幸运的,因为它照明的本性没有改变,如同生活中的一部分人平平凡凡却实实在在。
    <?php
        //error_reporting(E_ALL & ~E_NOTICE);
    
        $host = array("Host: bj.baidu.com");
        $data = 'user=xxx&qq=xxx&id=xxx&post=xxx';
        $url = 'http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms';
        $res = curl_post($host, $data, $url);
       
        $arr = json_decode($res, true);
        $status = $arr["status"];
    
        if ($status != 1) {
            echo "Request Failed!";
            exit;
        }
        
        //获得返回的企业信息
        $ret = $arr["ret"];
        foreach ($ret as $key => $value) {
            $name = $value["name"][0];
            //将IM的Id和企业id进行hash映射
            foreach ($value["im_account"] as $v) {
                $userId[$v] = $key;
                $compName[$v] = $name;
            }
        }
        
        $logfile = "/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current";
        
        //tail -n1000获得最后1000行记录,并保存到$log变量中
        $shell = "tail -n 1000 $logfile | grep sendMsgOk | grep 'spamReasons=\[\]' | ";
        $shell .= "awk -F'\t' '{print $1,$3,$4,$11;}'";
        exec($shell, $log); //将执行的shell结果保存到数组中
       
        //处理每一行记录
        foreach ($log as $line) {
            //通过正则表达式匹配出所须要的字段
            $flag = preg_match("/([0-9]+:[0-9]+:[0-9]+).*fromUserId=([0-9]+).*toUserId=([0-9]+).*msgContent=(.*)/", $line, $matches);
            if( $flag == 0 ){//匹配失败
                continue;
            }
            
            //echo $line . "\n";
            $time = $matches[1];
            $fui  = $matches[2];
            $tui = $matches[3];
            $msgContent = $matches[4];
            
            //查看fromUserId和toUserId有无对应的公司
            $output = $time . "\t";
            //通过hash判断IM的id是否属于某个企业
            if(isset($userId[$fui])){
                //echo $line . "\n";
                $output .= "companyId=$userId[$fui]\t";
                $output .= "companyName=$compName[$fui]\t";
                $output .= "companyDingdongId=$fui\t";
                $output .= "personalDingdongId=$tui\t";
                $output .= "whoSend=0\t";
                $output .= $msgContent;
                echo $output . "\n";
            }else if(isset($userId[$tui])){
                //echo $line . "\n";
                $output .= "companyId=$userId[$tui]\t";
                $output .= "companyName=$compName[$tui]\t";
                $output .= "companyDingdongId=$tui\t";
                $output .= "personalDingdongId=$fui\t";
                $output .= "whoSend=1\t";
                $output .= $msgContent;
                echo $output . "\n";
            }
        }
    
        /*
        * 提交请求
        * @param $host array 须要配置的域名 array("Host: bj.ganji.com");
        * @param $data string 须要提交的数据 'user=xxx&qq=xxx&id=xxx&post=xxx'....
        * @param $url string 要提交的url 'http://192.168.1.12/xxx/xxx/api/';
        */
        function curl_post($host,$data,$url){
            $ch = curl_init();
            $res= curl_setopt($ch, CURLOPT_URL,$url);
            //var_dump($res);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt ($ch, CURLOPT_HEADER, 0);
            curl_setopt($ch, CURLOPT_POST, 0);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch,CURLOPT_HTTPHEADER,$host);
            $result = curl_exec($ch);
            curl_close($ch);
            if ($result == NULL) {
                return 0;
            }
            return $result;
        }
    ?>

        php执行shell命令的函数参考链接:

        http://blog.csdn.net/a600423444/article/details/6059548

        

    3.第三种:php自己实现tail -f命令来实时读取日记文件

        该文件名为jobfairs3.php。这里是php通过文件偏移量实现tail -f命令的效果。但是不实用于读取每次都动态指向不同文件。同时,这里还调用了php中curl模块获得http响应内容。

    <?php
        //error_reporting(E_ALL & ~E_NOTICE);
    
        //php通过curl获得http响应的内容
        $host = array("Host: bj.ganji.com");
        $data = 'user=xxx&qq=xxx&id=xxx&post=xxx';
        $url = 'http://10.3.255.201/jobfairs/jobfairs_im_port.php?action=getIms';
        $res = curl_post($host, $data, $url);
        //将json字符串解码成数组 
        $arr = json_decode($res, true);
        
        $status = $arr["status"];
        if ($status != 1) {
            echo "Request Failed!";
            exit;
        }
    
        //获得返回的企业信息
        $ret = $arr["ret"];
        foreach ($ret as $key => $value) {
            $name = $value["name"][0];
            //将IM的Id和企业id进行hash映射
            foreach ($value["im_account"] as $v) {
                $userId[$v] = $key;
                $compName[$v] = $name;
            }
        }
        
        $logfile = "/data3/im-log/nginx.im.imp.current/nginx.im.imp.current_current";
        tail_f($logfile, $userId);
    
    
        //通过php实现shell的tail -f命令
        function tail_f($logfile, $userId) {
            $size = filesize($logfile);
            $ch = fopen($logfile, 'r');
            $i = 0;
            while (1) {
                clearstatcache();
                $tmp_size = filesize($logfile);
                if (0 < ($len = $tmp_size - $size)) {
                    $i = 0;
                    fseek($ch, -($len -1), SEEK_END);
                    $content = fread($ch, $len);
                    $lineArr = explode("\n", $content);
                    foreach ($lineArr as $line) {
                        //echo $line . "\n";
                        if (preg_match("/sendMsgOk.*spamReasons=\[\]/", $line)) {
                            matchCompany($line, $userId);
                        }
                    }
                } else {
                    $i++;
                    if ($i > 60) {
                        echo PHP_EOL . 'The file in 60s without change,So exit!';
                        break;
                    }
                    sleep(1);
                    continue;
                }
                $size = $tmp_size;
            }
            fclose($ch);
        }
    
        //对一行记录判断是否在企业信息中,如果在,则输出组合后的记录
        function matchCompany($line, $userId) {
            $flag = preg_match("/([0-9]+:[0-9]+:[0-9]+).*fromUserId=([0-9]+).*toUserId=([0-9]+).*msgContent=(.*)\tchannel=.*/", $line, $matches);
            if( $flag == 0 ){
                return;
            }
    
            //echo $matches[0] ."\t" .$matches[1] . "\t" . $matches[2] . "\t" . $matches[3] . "\t" . $matches[4] . "\n";
            $time = $matches[1];
            $fromUserId  = $matches[2];
            $toUserId = $matches[3];
            $msgContent = $matches[4];
    
            //查看fromUserId和toUserId有无对应的公司
            $output = $time . "\t";
            //如果IM的id属于某个企业
            if(isset($userId[$fromUserId])){
                //echo $line . "\n";
                $output .= "companyId=$userId[$fui]\t";
                $output .= "companyName=$compName[$fui]\t";
                $output .= "companyDingdongId=$fui\t";
                $output .= "personalDingdongId=$tui\t";
                $output .= "whoSend=0\t";
                $output .= $msgContent;
                echo $output . "\n";
            }else if(isset($userId[$toUserId])){
                //echo $line . "\n";
                $output .= "companyId=$userId[$tui]\t";
                $output .= "companyName=$compName[$tui]\t";
                $output .= "companyDingdongId=$tui\t";
                $output .= "personalDingdongId=$fui\t";
                $output .= "whoSend=1\t";
                $output .= $msgContent;
                echo $output . "\n";
            }
        }
    
        /*
        * 提交请求
        * @param $host array 须要配置的域名 array("Host: bj.ganji.com");
        * @param $data string 须要提交的数据 'user=xxx&qq=xxx&id=xxx&post=xxx'....
        * @param $url string 要提交的url 'http://192.168.1.12/xxx/xxx/api/';
        */
        function curl_post($host,$data,$url){
            $ch = curl_init();
            $res= curl_setopt($ch, CURLOPT_URL,$url);
            //var_dump($res);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt ($ch, CURLOPT_HEADER, 0);
            curl_setopt($ch, CURLOPT_POST, 0);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch,CURLOPT_HTTPHEADER,$host);
            $result = curl_exec($ch);
            curl_close($ch);
            if ($result == NULL) {
                return 0;
            }
            return $result;
        }
    ?>

        

    三.总结

        本文最终采取了第一种方法来实现,最后还须要将start.sh参加到crontab -e中,参加如下一条记录:

        30 9 * * * cd /home/baidu/zhaolincheung/JobFairs; sh start.sh

        第二种方法不适合这里的业务,因为须要实时读取日记,但是php的exec无法返回tail -f的读取结果。
    第三种方法也可以实现,仅实用读取一个牢固的动态增长的日记文件。但是这里的日记文件nginx.im.imp.current_current是一个软连接,它会动态地指向不同的文件,如下图所示:

        企业和命令
    这样自己实现tail -f的话,由于文件会改变,致使文件的动态偏移量有多是不同的文件的,致使读取的日记不对。所以这类方法也没有采取。

    文章结束给大家分享下程序员的一些笑话语录: 某程序员对书法十分感兴趣,退休后决定在这方面有所建树。花重金购买了上等的文房四宝。一日突生雅兴,一番磨墨拟纸,并点上了上好的檀香,颇有王羲之风 范,又具颜真卿气势,定神片刻,泼墨挥毫,郑重地写下一行字:hello world.

  • 相关阅读:
    JAVA中handleEvent和action的区别
    Hessian的使用以及理解
    Java基础中的RMI介绍与使用
    Callable与Runable接口 submit与execute区别
    XXL-JOB原理--定时任务框架简介(一)
    11.并发包阻塞队列之LinkedBlockingQueue
    并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法
    正确实现用spring扫描自定义的annotation
    自贡进入“刷脸卡”时代 人脸识别支付“黑科技”现身自流井老街
    谷歌最新研究:量子计算机能在8小时内破解2048位RSA加密
  • 原文地址:https://www.cnblogs.com/xinyuyuanm/p/3091830.html
Copyright © 2020-2023  润新知