美文网首页
【C/C++】项目_8_数据挖掘/HTTP协议/非结构化数据存储

【C/C++】项目_8_数据挖掘/HTTP协议/非结构化数据存储

作者: linuxCoder | 来源:发表于2020-07-15 16:07 被阅读0次

    @TOC


    1. 数据挖掘子系统:rc.local

    如下数据挖掘dmining和数据交换exptables差不多(exptables自己设计数据表有keyid字段,从其他地方拿数据未必有keyid字段需要指定,也就是数据是增量的,但不提供增量提取方式即没有keyid字段),将数据从数据库中取出导入文件(执行一个sql拿出数据)。vi /etc/rc.local(随操作系统自启动)

    在这里插入图片描述
    vi /htidc/gz.....sh上面如下,如下要写的其实就是< selectsql >字段里内容。比如取近一小时数据,每次取到都有重复但在入库时处理重复就行,获取到的数据生成xml再入自己库
    在这里插入图片描述
    在这里插入图片描述
    //dminoracle.cpp
    #include "_public.h"
    #include "_ooci.h"   // oracle数据库是_ooci.h,mysql数据库换成_mysql.h,PostgreSQL数据库换成_postgresql.h
    // 主程序的参数
    struct st_arg
    {
      char connstr[101];
      char charset[51];
      char tname[51];
      char cols[1001];
      char fieldname[1001];
      char fieldlen[501];
      int  exptype;
      char andstr[501];
      char bname[51];
      char ename[51];
      char idfieldname[51];
      char idfilename[301];
      char exppath[301];
      int  timetvl;
    } starg;
    CLogFile logfile;
    connection conn;
    // 本程序的业务流程主函数
    bool _dmintables();
    void EXIT(int sig);
    vector<string> vfieldname; // 存放拆分fieldname后的容器
    vector<int>    vfieldlen;  // 存放拆分fieldlen后的容器
    int maxfieldlen;           // 存放fieldlen的最大值
    void SplitFields();        // 拆分fieldname和fieldlen
    // 显示程序的帮助
    void _help(char *argv[]);
    long maxkeyid;   // 已挖掘数据的最大的id
    bool LoadMaxKeyid(); // 从记录已获取数据最大id的文件中加载已挖掘数据的最大的id
    bool UptMaxKeyid();  // 更新已挖掘数据的最大的id到文件中  
    // 把xml解析到参数starg结构中
    bool _xmltoarg(char *strxmlbuffer);
    
    int main(int argc,char *argv[])
    {
      if (argc!=3) { _help(argv); return -1; }
      // 关闭全部的信号和输入输出
      CloseIOAndSignal();
      // 处理程序退出的信号
      signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
      if (logfile.Open(argv[1],"a+")==false)
      {
        printf("打开日志文件失败(%s)。\n",argv[1]); return -1;
      }
      // 把xml解析到参数starg结构中
      if (_xmltoarg(argv[2])==false) return -1;
      while (true)
      {
        // 连接数据库
        if (conn.connecttodb(starg.connstr,starg.charset) != 0)
        {
          logfile.Write("connect database %s failed.\n",starg.connstr); sleep(starg.timetvl); continue;
        }
        // logfile.Write("export table %s.\n",starg.tname);
        // 挖掘数据的主函数
        if (_dmintables() == false) logfile.Write("export tables failed.\n");
        conn.disconnect();   // 断开与数据库的连接
        sleep(starg.timetvl);
      }
      return 0;
    }
    void EXIT(int sig)
    {
      logfile.Write("程序退出,sig=%d\n\n",sig);
    
      exit(0);
    }
    
    // 显示程序的帮助
    void _help(char *argv[])
    {
      printf("\n");
      printf("Using:/htidc/public/bin/dminoracle logfilename xmlbuffer\n\n");
    
      printf("增量挖掘示例:\n");
      printf("Sample:/htidc/public/bin/dminoracle /log/shqx/dminoracle_surfdata_from_qx.log \"<connstr>shqx/pwdidc@snorcl11g_198</connstr><charset>Simplified Chinese_China.ZHS16GBK</charset><tname>T_SURFDATA</tname><cols>obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis</cols><fieldname>obtid,ddatetime,t,p,u,wd,wf,r,vis</fieldname><fieldlen>5,14,8,8,8,8,8,8,8</fieldlen><exptype>1</exptype><andstr> and obtid in ('59293','50745')</andstr><bname>SURFDATA_</bname><ename>_from_qx</ename><idfilename>/data/dmin/SURFDATA_from_qx.txt</idfilename><idfieldname>keyid</idfieldname><exppath>/data/shqx/sdata/fromqx</exppath><timetvl>30</timetvl>\"\n\n");
      printf("全量挖掘示例:\n");
      printf("Sample:/htidc/public/bin/dminoracle /log/shqx/dminoracle_obtcode_from_qx.log \"<connstr>shqx/pwdidc@snorcl11g_198</connstr><charset>Simplified Chinese_China.ZHS16GBK</charset><tname>T_OBTCODE</tname><cols>obtid,obtname,provname,lat,lon,height</cols><fieldname>obtid,obtname,provname,lat,lon,height</fieldname><fieldlen>5,30,30,8,8,8</fieldlen><exptype>2</exptype><andstr> and rsts=1 and obtid in ('59293','50745')</andstr><bname>OBTCODE_</bname><ename>_from_qx</ename><exppath>/data/shqx/sdata/fromqx</exppath><timetvl>300</timetvl>\"\n\n");
    
      printf("本程序是数据中心的公共功能模块,从其它业务系统的数据库中挖掘数据,用于入库到数据中心。\n");
      printf("logfilename是本程序运行的日志文件。\n");
      printf("xmlbuffer为文件传输的参数,如下:\n");
      printf("数据库的连接参数 <connstr>shqx/pwdidc@snorcl11g_198</connstr>\n");
      printf("数据库的字符集 <charset>Simplified Chinese_China.ZHS16GBK</charset> 这个参数要与数据源数据库保持>一致,否则会出现中文乱码的情况。\n");
      printf("待挖掘数据的表名 <tname>T_SURFDATA</tname>\n");
      printf("需要挖掘字段的列表 <cols>obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis</cols> 可以采用函数。\n");
      printf("挖掘字段的别名列表 <fieldname>obtid,ddatetime,t,p,u,wd,wf,r,vis</fieldname> 必须与cols一一对应。\n");
      printf("挖掘字段的长度列表 <fieldlen>5,14,8,8,8,8,8,8,8</fieldlen> 必须与cols一一对应。\n");
      printf("挖掘数据的方式 <exptype>1</exptype> 1-增量挖掘;2-全量挖掘,如果是增量挖掘,要求表一定要有表达记录序号的id字段。\n");
      printf("挖掘数据的附加条件 <andstr> and obtid in ('59293','50745')</andstr> 注意,关键字and不能少。\n");
      printf("数据文件的命名的前部分 <bname>SURFDATA_</bname>\n");
      printf("数据文件的命名的后部分 <ename>_from_qx</ename>\n");
      printf("挖掘数据表记录号字段名 <idfieldname>keyid</idfieldname> 当exptype=1时该参数有效。\n");
      printf("已挖掘数据id保存的文件名 <idfilename>/data/dmin/SURFDATA_from_qx.txt</idfilename> 当exptype=1时该参数有效。\n");
      printf("挖掘文件存放的目录 <exppath>/data/shqx/sdata/fromqx</exppath>\n");
      printf("挖掘数据的时间间隔 <timetvl>30</timetvl> 单位:秒,建议大于10。\n");
      printf("以上参数,除了idfieldname、idfilename和andstr,其它字段都不允许为空。\n\n\n");
    }
    
    // 把xml解析到参数starg结构中
    bool _xmltoarg(char *strxmlbuffer)
    {
      memset(&starg,0,sizeof(struct st_arg));
      GetXMLBuffer(strxmlbuffer,"connstr",starg.connstr);
      if (strlen(starg.connstr)==0) { logfile.Write("connstr is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"charset",starg.charset);
      if (strlen(starg.charset)==0) { logfile.Write("charset is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"tname",starg.tname);
      if (strlen(starg.tname)==0) { logfile.Write("tname is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"cols",starg.cols);
      if (strlen(starg.cols)==0) { logfile.Write("cols is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"fieldname",starg.fieldname);
      if (strlen(starg.fieldname)==0) { logfile.Write("fieldname is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"fieldlen",starg.fieldlen);
      if (strlen(starg.fieldlen)==0) { logfile.Write("fieldlen is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"exptype",&starg.exptype);
      if ( (starg.exptype!=1) && (starg.exptype!=2) ) { logfile.Write("exptype is not in (1,2).\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"andstr",starg.andstr);
      if (strlen(starg.andstr)==0) { logfile.Write("andstr is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"bname",starg.bname);
      if (strlen(starg.bname)==0) { logfile.Write("bname is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"ename",starg.ename);
      if (strlen(starg.ename)==0) { logfile.Write("ename is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"idfieldname",starg.idfieldname);
      if ( (starg.exptype==1) && (strlen(starg.idfieldname)==0) ) { logfile.Write("idfieldname is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"idfilename",starg.idfilename);
      if ( (starg.exptype==1) && (strlen(starg.idfilename)==0) ) { logfile.Write("idfilename is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"exppath",starg.exppath);
      if (strlen(starg.exppath)==0) { logfile.Write("exppath is null.\n"); return false; }
      GetXMLBuffer(strxmlbuffer,"timetvl",&starg.timetvl);
      if (starg.timetvl==0) { logfile.Write("timetvl is null.\n"); return false; }
      // 拆分fieldname和fieldlen
      SplitFields();
      // 判断fieldname和fieldlen中元素的个数一定要相同
      if (vfieldname.size() != vfieldlen.size() ) { logfile.Write("fieldname和fieldlen的元素个数不同。.\n"); return false; }
      return true;
    }
    
    //////////////////////////////////////////////////1.本程序的业务流程主函数
    bool _dmintables()
    {
      // 从记录已获取数据最大id的文件中加载已挖掘数据的最大的id
      if (LoadMaxKeyid()==false) { logfile.Write("LoadMaxKeyid() failed.\n"); return false; }
    
      // 生成挖掘数据的SQL语句
      char strsql[4096];   
      char fieldvalue[vfieldname.size()][maxfieldlen+1]; // 输出变量定义为一个二维数组
      memset(strsql,0,sizeof(strsql));
      if (starg.exptype==1)
        sprintf(strsql,"select %s,%s from %s where 1=1 and %s>%ld %s order by %s",starg.cols,starg.idfieldname,starg.tname,starg.idfieldname,maxkeyid,starg.andstr,starg.idfieldname);
      else
        sprintf(strsql,"select %s from %s where 1=1 %s",starg.cols,starg.tname,starg.andstr);
      sqlstatement stmt(&conn);
      stmt.prepare(strsql);
      for (int ii=0;ii<vfieldname.size();ii++)
      {
        stmt.bindout(ii+1,fieldvalue[ii],vfieldlen[ii]);
      }
      // 如果是增量挖掘,还要绑定id字段
      if (starg.exptype==1) stmt.bindout(vfieldname.size()+1,&maxkeyid);
      
      // 执行挖掘数据的SQL
      if (stmt.execute() != 0)
      {
        logfile.Write("select %s failed.\n%s\n%s\n",starg.tname,stmt.m_cda.message,stmt.m_sql); return false;
      }
      int  iFileSeq=1;   // 待生成文件的序号
      char strFileName[301],strLocalTime[21];
      CFile File;
      while (true)
      {
        memset(fieldvalue,0,sizeof(fieldvalue));   
        if (stmt.next() !=0) break;
        // 把数据写入文件
        if (File.IsOpened()==false)
        {
          memset(strLocalTime,0,sizeof(strLocalTime));
          LocalTime(strLocalTime,"yyyymmddhh24miss");
          memset(strFileName,0,sizeof(strFileName));
          sprintf(strFileName,"%s/%s%s%s_%d.xml",starg.exppath,starg.bname,strLocalTime,starg.ename,iFileSeq++);
          if (File.OpenForRename(strFileName,"w")==false)
          {
            logfile.Write("File.OpenForRename(%s) failed.\n",strFileName); return false;
          }
          File.Fprintf("<data>\n");
        }
        for (int ii=0;ii<vfieldname.size();ii++)
        {
          File.Fprintf("<%s>%s</%s>",vfieldname[ii].c_str(),fieldvalue[ii],vfieldname[ii].c_str());
        }
        File.Fprintf("<endl/>\n");
        
    /////////////////////////////////////////////////////////1.1 1000条记录写入一个文件完成
        if (stmt.m_cda.rpc%1000==0)
        {
          File.Fprintf("</data>\n");
          if (File.CloseAndRename()==false)
          {
            logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;
          }
          // 更新已挖掘数据的最大的id到文件中
          if (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }
          logfile.Write("create file %s ok.\n",strFileName);
        }
      }
    
    /////////////////////////////////////////////////////1.2 不够1000条的写入一个文件
      if (File.IsOpened()==true)
      {
        File.Fprintf("</data>\n");
        if (File.CloseAndRename()==false)
        {
          logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;
        }
        // 更新已挖掘数据的最大的id到文件中
        if (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }
        logfile.Write("create file %s ok.\n",strFileName);
      }
      if (stmt.m_cda.rpc>0) logfile.Write("本次挖掘了%d条记录。\n",stmt.m_cda.rpc);
      return true;
    }
    
    /////////////////////////////////////////2.拆分fieldname和fieldlen
    void SplitFields()
    {
      vfieldname.clear(); vfieldlen.clear(); maxfieldlen=0;  
      CCmdStr CmdStr;
      CmdStr.SplitToCmd(starg.fieldname,",");
      vfieldname.swap(CmdStr.m_vCmdStr);
    
      int ifieldlen=0;
      CmdStr.SplitToCmd(starg.fieldlen,",");
      for (int ii=0;ii<CmdStr.CmdCount();ii++)
      {  
        CmdStr.GetValue(ii,&ifieldlen);
        if (ifieldlen>maxfieldlen) maxfieldlen=ifieldlen;   // 得到fieldlen的最大值
        vfieldlen.push_back(ifieldlen);
      }
    }
    
    /////////////////////////////3.从记录已获取数据最大id的文件中加载已挖掘数据的最大的id
    bool LoadMaxKeyid()
    {
      if (starg.exptype!=1) return true;
      CFile File;
      if (File.Open(starg.idfilename,"r")==false) 
      {
        logfile.Write("注意,%s文件不存在,程序将从新开始挖掘数据。\n",starg.idfilename); return true;
      }
      char strBuf[21];
      memset(strBuf,0,sizeof(strBuf));
      File.Fread(strBuf,20);
      maxkeyid=atol(strBuf);
      logfile.Write("maxkeyid=%d\n",maxkeyid);
      return true;
    }
    
    ////////////////////////////4.更新已挖掘数据的最大的id到文件中
    bool UptMaxKeyid()
    {
      if (starg.exptype!=1) return true;
      CFile File;
      if (File.Open(starg.idfilename,"w")==false) 
      {
        logfile.Write("File.Open(%s) failed.\n",starg.idfilename); return false;
      }
      File.Fprintf("%ld",maxkeyid);
      return true;
    }
    
    在这里插入图片描述
    在这里插入图片描述

    2.HTTP协议:wget,转义

    浏览器输入网址后回车,就是向服务端(web系统)发送请求,浏览器就是客户端


    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    注册后会分配一个appkeyid加在如上url后面。数据共享平台也有一个web系统(服务端)用java做的,vi /etc/rc.local如下


    在这里插入图片描述
    如下http客户端访问不了,startup后可访问
    在这里插入图片描述
    按照http格式向网站发报文也会得到回应,如下http客户端
    在这里插入图片描述
    在这里插入图片描述

    wgetclient是用wget命令下载,httpclient下载的内容有时候会有断行,wget下载不会,优先wget,wget下载不了的用http。wget支持http,https,ftp,sftp等协议。linux下同步文件rsync,curl,wget,ftp,sftp


    在这里插入图片描述
    如上调用接口,如下还可以右击网页上图片复制图片地址
    在这里插入图片描述
    程序在后台跑,有新图就拿下来,wgetclient将网页内容全弄下来,搞清图片命名规律进行解析
    //wgetclient.cpp
    #include "_public.h"
    void EXIT(int sig);
    CLogFile       logfile;
    int main(int argc, char *argv[])
    {
      if(argc!=6)
      {
        printf("Usage:%s weburl tmpfilename outputfilename logfilename charset\n",argv[0]); 
        printf("本程序用于获取WEB网页的内容。\n");
        printf("weburl 网页WEB的地址。\n");
        printf("tmpfilename 获取到的网页的内容存放的全路径的临时文件名,该文件可能是utf-8或其它编码。\n");
        printf("outputfilename 最终的输出文件全路径文件名,该文件是gb18030编码,注意tmpfilename被转换为outputfilename后,tmpfilename文件被自动删除。\n");
        printf("logfilename 本程序的运行产生的日志文件名。\n");
        printf("charset 网页的字符集,如utf-8\n\n");
        exit(1);
      }
    
      // 关闭全部的信号和输入输出
      // 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
      // 但请不要用 "kill -9 +进程号" 强行终止
      CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
    
      // 打开日志文件
      if (logfile.Open(argv[4],"a+") == false)
      {
        printf("logfile.Open(%s) failed.\n",argv[4]); return -1;
      }
    
      MKDIR(argv[2],true); MKDIR(argv[3],true);
      char strweburl[3001];
      memset(strweburl,0,sizeof(strweburl));
      strncpy(strweburl,argv[1],3000);
     
      char strcmd[3024];
      memset(strcmd,0,sizeof(strcmd));
      snprintf(strcmd,3000,"/usr/bin/wget -c -q -O %s \"%s\" 1>>/dev/null 2>>/dev/null",argv[2],strweburl);
      system(strcmd);
      logfile.Write("%s\n",strcmd);
    
      char strfilenametmp[301];
      memset(strfilenametmp,0,sizeof(strfilenametmp));
      snprintf(strfilenametmp,300,"%s.tmp",argv[3]);
    
       // 把获取到的网页转换为中文
      memset(strcmd,0,sizeof(strcmd));
      snprintf(strcmd,256,"iconv -c -f %s -t gb18030 %s -o %s",argv[5],argv[2],strfilenametmp);
      system(strcmd);
      logfile.Write("%s\n",strcmd);
      REMOVE(argv[2]);   // 删除临时文件 
      RENAME(strfilenametmp,argv[3]);
      return 0;
    }
    
    void EXIT(int sig)
    {
      if (sig > 0) signal(sig,SIG_IGN);
      logfile.Write("catching the signal(%d).\n",sig);
      logfile.Write("wgetclient exit.\n");
      exit(0);
    }
    
    /*
    除了普通的字母,数字,中文,还有特殊字符,但是规范的使用应该是使用字符转义。
    十六进制值 
    1. +  URL 中+号表示空格 %2B 
    2. 空格 URL中的空格可以用+号或者编码 %20 
    3. /  分隔目录和子目录 %2F  
    4. ?  分隔实际的 URL 和参数 %3F  
    5. % 指定特殊字符 %25  
    6. # 表示书签 %23  
    7. & URL 中指定的参数间的分隔符 %26  
    8. = URL 中指定参数的值 %3D 
    */
    
    //wgetrain24.cpp
    #include "_public.h"
    void EXIT(int sig);
    CLogFile       logfile;
    bool GetURL(char *strBuffer,char *strURL,char *strFileName);
    int main(int argc, char *argv[])
    {
      if(argc!=4)
      {
        printf("Usage:%s logfilename tmpfilename outputfilename\n",argv[0]); 
        printf("Sample:./wgetrain24 /log/shqx/wgetrain24.log /data/wgettmp /data/wfile/zhrain24\n\n");
        printf("本程序用于从中国天气网获取逐小时降雨量实况图。\n");
        printf("中国天气网的url是http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\n");
        printf("如果中国天气网的url改变,程序也在做改动。\n");
        printf("logfilename 本程序的运行产生的日志文件名。\n");
        printf("tmpfilename 本程序运行产生的临时文件存放的目录。\n");
        printf("获取逐小时降雨量实况图存放的目录。\n\n");
        exit(1);
      }
      // 关闭全部的信号和输入输出
      // 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
      // 但请不要用 "kill -9 +进程号" 强行终止
      CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
      // 打开日志文件
      if (logfile.Open(argv[1],"a+") == false)
      {
        printf("logfile.Open(%s) failed.\n",argv[1]); return -1;
      }
    
      MKDIR(argv[2],false); MKDIR(argv[3],false);
      while (true)
      {
        // 调用wgetclient获取网页内容
        char strwgetclient[2001];
        memset(strwgetclient,0,sizeof(strwgetclient));
        snprintf(strwgetclient,2000,"/htidc/public/bin/wgetclient \"http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\" %s/wgetclient_%d.tmp  %s/wgetclient_%d.html %s/wgetclient.log utf-8",argv[2],getpid(),argv[2],getpid(),argv[2]);
        system(strwgetclient);
        // logfile.Write("%s\n",strwgetclient);
      
        // 打开网页内容文件
        char stroutputfile[301];
        memset(stroutputfile,0,sizeof(stroutputfile));
        snprintf(stroutputfile,300,"%s/wgetclient_%d.html",argv[2],getpid());
        CFile File;
        if (File.Open(stroutputfile,"r")==false)
        {
          logfile.Write("File.Open(%s) failed.\n",stroutputfile); sleep(60); continue;
        }    
        char strBuffer[1001],strURL[501],strFullFileName[301],strFileName[101];  
       
        // 得到全部的图片文件名
        while (true)
        {
          memset(strBuffer,0,sizeof(strBuffer));
          memset(strURL,0,sizeof(strURL));
          memset(strFullFileName,0,sizeof(strFullFileName));
          memset(strFileName,0,sizeof(strFileName));  
          if (File.Fgets(strBuffer,1000)==false) break;  
          if (MatchFileName(strBuffer,"*PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG*")==false) continue;  
          // logfile.Write("%s",strBuffer);  
          // 解析出url和文件名
          if (GetURL(strBuffer,strURL,strFileName)==false) continue; 
          // 文件已存在,不采集
          snprintf(strFullFileName,300,"%s/%s",argv[3],strFileName);
          if (access(strFullFileName,F_OK)==0) continue;  
          // 调用wget获取文件
          logfile.Write("download %s ",strFileName);
          memset(strwgetclient,0,sizeof(strwgetclient));
          snprintf(strwgetclient,500,"wget \"%s\" -o %s/wgetrain24.log -O %s",strURL,argv[2],strFullFileName);
          system(strwgetclient);  
          if (access(strFullFileName,F_OK)==0) logfile.WriteEx("ok.\n");
          else logfile.WriteEx("failed.\n");
        }  
        File.CloseAndRemove();
        sleep(60);
      }
      return 0;
    }
    
    bool GetURL(char *strBuffer,char *strURL,char *strFileName)
    {
      char *start,*end;
      start=end=0;
      if ((start=strstr(strBuffer,"http"))==0) return false;
      if ((end=strstr(start,"\""))==0) return false; //找双引号
      strncpy(strURL,start,end-start);
      strcpy(strFileName,strstr(strURL,"PWCP"));
      return true;
    }
    void EXIT(int sig)
    {
      if (sig > 0) signal(sig,SIG_IGN);
      logfile.Write("catching the signal(%d).\n",sig);
      logfile.Write("wgetclient exit.\n");
      exit(0);
    }
    
    在这里插入图片描述

    3.非结构化数据存储:blob,pzhrain24file

    在这里插入图片描述
    // 本程序演示如何把磁盘文件的文本文件写入Oracle的BLOB字段中。
    //filetoblob.cpp,实时生成的不要存oracle的blob字段
    #include "_ooci.h"
    int main(int argc,char *argv[])
    {
      // 数据库连接池
      connection conn;  
      // 连接数据库,返回值0-成功,其它-失败
      // 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
      if (conn.connecttodb("scott/tiger@snorcl11g_198","Simplified Chinese_China.ZHS16GBK") != 0)
      {
        printf("connect database %s failed.\n%s\n","scott/tiger@orcl",conn.m_cda.message); return -1;
      }  
      // SQL语言操作类
      sqlstatement stmt(&conn);  
      // 为了方便演示,把goods表中的记录全删掉,再插入一条用于测试的数据。
      // 不需要判断返回值
      stmt.prepare("\
        BEGIN\
          delete from goods;\
          insert into goods(id,name,pic) values(1,'商品名称',empty_blob());\
        END;");  
      // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
      if (stmt.execute() != 0)
      {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;
      }
      // 使用游标从goods表中提取id为1的pic字段
      // 注意了,同一个sqlstatement可以多次使用
      // 但是,如果它的sql改变了,就要重新prepare和bindin或bindout变量
      stmt.prepare("select pic from goods where id=1 for update");
      stmt.bindblob();
      // 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
      if (stmt.execute() != 0)
      {
        printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;
      }
      // 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
      if (stmt.next() != 0) return 0;  
      // 把磁盘文件pic_in.jpg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。
      if (stmt.filetolob((char *)"pic_in.jpg") != 0)
      {
        printf("stmt.filetolob() failed.\n%s\n",stmt.m_cda.message); return -1;
      }
      // 提交事务
      conn.commit();
      return 0;
    }
    
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    // pzhrain24file.cpp
    #include "_public.h"
    #include "_ooci.h"
    #include "_shqx.h"
    CLogFile logfile;
    CDir Dir;
    // 处理数据文件
    bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5);
    connection conn;
    void EXIT(int sig);
    
    int main(int argc,char *argv[])
    {
      if (argc!=7)
      {
        printf("\n本程序用于处理全国逐小时雨量实况图片文件。\n\n");
        printf("/htidc/shqx/bin/pzhrain24file logfilename connstr srcpathname dstpathname tname timetvl\n");
        printf("例如:/htidc/shqx/bin/pzhrain24file /log/shqx/pzhrain24file.log shqx/pwdidc@snorcl11g_198 /data/wfile/zhrain24 /qxfile/zhrain24 T_ZHRAIN24 30\n");
        printf("logfilename 本程序运行的日志文件名。\n");
        printf("connstr 数据库的连接参数。\n");
        printf("srcpathname 原始文件存放的目录,文件命名如PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20191101070000000.JPG。\n");
        printf("dstpathname 目标文件存放的目录,文件按yyyy/mm/dd组织目录,重命名为zhrain24_yyyymmddhh24miss.jpg。\n");
        printf("tname 数据存放的表名。\n");
        printf("timetvl 本程序运行的时间间隔,单位:秒。\n");
        return -1;
      }
      // 关闭全部的信号和输入输出
      CloseIOAndSignal();
      // 处理程序退出的信号
      signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
    
      if (logfile.Open(argv[1],"a+")==false)
      {
        printf("打开日志文件失败(%s)。\n",argv[1]); return -1;
      }
      logfile.Write("程序启动。\n");
      while (true)
      {
        // logfile.Write("开始扫描目录。\n");
        // 扫描数据文件存放的目录,只匹配"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG"
        if (Dir.OpenDir(argv[3],"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG",1000,true,true)==false)
        {
          logfile.Write("Dir.OpenDir(%s) failed.\n",argv[3]); sleep(atoi(argv[6])); continue;
        }
        // 逐个处理目录中的数据文件
        while (true)
        {
          if (Dir.ReadDir()==false) break;
      
          // 处理数据文件
          if (_pzhrain24file(argv[2],argv[4],argv[5])==false) 
          {
            logfile.WriteEx("失败。\n"); continue;
          }
        }
        // 断开与数据库的连接
        if (conn.m_state==1) conn.disconnect(); 
        sleep(atoi(argv[6]));
      }
      return 0;
    }
    void EXIT(int sig)
    {
      logfile.Write("程序退出,sig=%d\n\n",sig);
      exit(0);
    }
         
    /////////////////////////////////////////////////处理数据文件
    bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5)
    {
      char strddatetime[21];   // 文件的数据时间,格式yyyymmddhh24miss
      memset(strddatetime,0,sizeof(strddatetime));
      strncpy(strddatetime,strstr(Dir.m_FileName,"20"),14);
      
      //搜索文件名PWCP_TWC…中20,后面取14位,重命名为zhrain24_%s.jpg
      char strdstfilename[301];  // 目标文件名,不带路径
      memset(strdstfilename,0,sizeof(strdstfilename));
      snprintf(strdstfilename,300,"zhrain24_%s.jpg",strddatetime);
      
      char strdstfilepath[301];  // 目标文件存放的目录
      memset(strdstfilepath,0,sizeof(strdstfilepath));
      snprintf(strdstfilepath,300,"%s/",strargv4);
      strncat(strdstfilepath,strddatetime,4);     strcat(strdstfilepath,"/");  // 年的子目录
      strncat(strdstfilepath,strddatetime+4,2);   strcat(strdstfilepath,"/");  // 月的子目录
      strncat(strdstfilepath,strddatetime+6,2);   strcat(strdstfilepath,"/");  // 日的子目录
    
      char strfulldstfilename[301]; // 目标文件名,全路径
      memset(strfulldstfilename,0,sizeof(strfulldstfilename));
      snprintf(strfulldstfilename,300,"%s%s",strdstfilepath,strdstfilename);
    
      // 如果文件已处理(目标文件已存在),直接返回成功。
      if (access(strfulldstfilename,F_OK) == 0) return true;
    
      if (conn.m_state==0)
      {
        if (conn.connecttodb(strargv2,"Simplified Chinese_China.ZHS16GBK")!=0)
        {
          logfile.Write("connect database(%s) failed.\n%s\n",strargv2,conn.m_cda.message); return false;
        }
        // logfile.Write("连接数据库成功。\n");
      }
      
      // 把源文件复制到目标文件
      if (COPY(Dir.m_FullFileName,strfulldstfilename)==false) 
      {
        logfile.Write("复制文件COPY(%s,%s)...failed.\n",Dir.m_FullFileName,strfulldstfilename); return false;
      }
    
      // 把非结构化数据文件写入oracle数据库的表中
      if (FileToTable(&conn,&logfile,strargv5,strfulldstfilename,strddatetime)!=0)
      {
        logfile.Write("把文件%s存入%s...failed.\n",strfulldstfilename,strargv5);  return false;
      }
      logfile.Write("把文件%s存入%s...ok.\n",strfulldstfilename,strargv5);  
      return true;
    }
    
    在这里插入图片描述
    在这里插入图片描述

    如下是目标文件夹并做了重命名,如上程序中COPY函数,是复制并不删除,因为采集文件是增量采集,删除了又重新下载


    在这里插入图片描述
    在这里插入图片描述

    数据库客户端服务端性能不够用文件传输系统


    在这里插入图片描述

    4.数据管理子系统:数据字典表,分表

    Oracle数据字典:是一组表和视图的结构(就像仓库里有什么,每种有多少,哪些空间可用等日记)。自己创建的表,oracle会将这表的信息自动写入系统表,系统表上再创建一视图供客户查看。数据字典中的表是不能被访问修改(自己用的),但是可以访问数据字典的视图就可以知道自己创建的表的详细信息

    在这里插入图片描述
    在这里插入图片描述
    数据字典除了以下三类还有V$_表示性能或参数设置相关数据比如数据库的字符集,会话数,进程数等等。tab也是视图,tab里面表,视图,同义词都可以查到。若是select * from dba_tables;必须是dba用户才可以查
    在这里插入图片描述
    如下是常用的数据字典表
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    数据量大了会出现性能和数据管理,迁移,备份问题。假如有1亿数据量,不能用exp导出,一个文件有几百G导出要一天。索引设计要合理,不然table scan跑不动。一亿以下的数据单表存放,一亿以上的数据考虑分表,十亿虽然比一亿容量大十倍,但性能不会下降十倍,因为索引(比如按首字母找名字)
    在这里插入图片描述
    如下并行数据库分布式查询可解决多表性能低的问题
    在这里插入图片描述
    写一些程序实现数据管理,数据删除(deletables.cpp)或放历史表等等。三种表数据结构一样,迁移数据可写成一个通用功能模块程序。若是不同表,只知道表名去查询数据字典得到数据结构
    如下hsmtable.cpp(和deletetable.cpp像)数据源表就是要把数据从哪个表迁移出来,列名从数据字典取出,把列拼成一个字符串,获取列名后把数据rowid从源表查出来,生成插入目的表sql,删除源表sql
    //hsmtable.cpp
    #include "_public.h"
    #include "_ooci.h"
    char logfilename[301];
    char connstr[101];
    char srctname[51];
    char dsttname[51];
    char where[1024];
    char hourstr[101];
    char localhour[21];
    int  maxcount=1;
    connection conn;
    CLogFile logfile;
    void EXIT(int sig);
    // 获取dsttname表全部的列
    char strColumnStr[2048];
    bool GetColumnStr();
    // 显示程序的帮助
    void _help(char *argv[]);
    bool _hsmtables();
    
    int main(int argc,char *argv[])
    {
      if (argc != 2) { _help(argv); return -1; }
      memset(logfilename,0,sizeof(logfilename));
      memset(connstr,0,sizeof(connstr));
      memset(srctname,0,sizeof(srctname));
      memset(dsttname,0,sizeof(dsttname));
      memset(where,0,sizeof(where));
      memset(hourstr,0,sizeof(hourstr));
    
      GetXMLBuffer(argv[1],"logfilename",logfilename,300);
      GetXMLBuffer(argv[1],"connstr",connstr,100);
      GetXMLBuffer(argv[1],"srctname",srctname,50);
      GetXMLBuffer(argv[1],"dsttname",dsttname,50);
      GetXMLBuffer(argv[1],"where",where,1000);
      GetXMLBuffer(argv[1],"maxcount",&maxcount);
      GetXMLBuffer(argv[1],"hourstr",hourstr,2000);
    
      if (strlen(logfilename) == 0) { printf("logfilename is null.\n"); return -1; }
      if (strlen(connstr) == 0)     { printf("connstr is null.\n"); return -1; }
      if (strlen(srctname) == 0)    { printf("srctname is null.\n"); return -1; }
      if (strlen(dsttname) == 0)    { printf("dsttname is null.\n"); return -1; }
      if (strlen(where) == 0)       { printf("where is null.\n"); return -1; }
     if ( (maxcount<1) || (maxcount>1000) ) { printf("maxcount %d is invalid,should in 1-1000.\n",maxcount); return -1; }
      if (strlen(hourstr) == 0)     { printf("hourstr is null.\n"); return -1; }
      // 关闭全部的信号和输入输出
      CloseIOAndSignal();
      // 处理程序退出的信号
      signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
      // 打开日志文件
      if (logfile.Open(logfilename,"a+") == false)
      {
        printf("logfile.Open(%s) failed.\n",logfilename); return -1;
      }
      while (true)
      {
        // 判断当前时间是否在启动时间之内
        memset(localhour,0,sizeof(localhour));
        LocalTime(localhour,"hh24");
        if (strstr(hourstr,localhour)==0) { sleep(60); continue; }
        // 连接数据库
        if (conn.connecttodb(connstr,"Simplified Chinese_China.ZHS16GBK") != 0)
        {
          logfile.Write("connect database %s failed.\n",connstr); sleep(60); continue; 
        }
        logfile.Write("from table %s to %s.\n",srctname,dsttname);
        if (_hsmtables() == false) logfile.Write("_hsmtables failed.\n"); 
        conn.disconnect();
        sleep(60); 
      }
      return 0;
    }
    void EXIT(int sig)
    {
      printf("程序退出,sig=%d\n\n",sig);
      exit(0);
    }
    // 显示程序的帮助
    void _help(char *argv[])
    {
      printf("\nUsing:/htidc/public/bin/hsmtables \"<logfilename>/log/shqx/hsmtables_SURFDATA.log</logfilename><connstr>shqx/pwdidc@snorcl11g_198</connstr><srctname>T_SURFDATA</srctname><dsttname>T_SURFDATA_HIS</dsttname><where>where ddatetime<sysdate-10</where><maxcount>500</maxcount><hourstr>23,01,02,03,04,05,06</hourstr>\"\n\n");
    
      printf("这是一个工具程序,用于清理表中的数据。\n");
      printf("<logfilename>/log/shqx/hsmtables_SURFDATA.log</logfilename> 本程序运行日志文件名。\n");
      printf("<connstr>szidc/pwdidc@SZQX_10.153.97.251</connstr> 目的数据库的连接参数。\n");
      printf("<srctname>T_SURFDATA</srctname> 数据源表名。\n");
      printf("<dsttname>T_SURFDATA_HIS</dsttname> 目的数据表名。\n");
      printf("<where>where ddatetime<sysdate-10</where> 待迁移数据的条件。\n");
      printf("<maxcount>500</maxcount> 单次执行数据迁移的记录数,取值在1-1000之间。\n");
      printf("<hourstr>23,01,02,03,04,05,06</hourstr> 本程序启动的时次,小时,时次之间用半角的逗号分隔开。\n\n");
      return;
    }
    
    bool _hsmtables()
    {
      // 获取dsttname表全部的列
      if (GetColumnStr() == false) return false;
      int  ccount=0;
      char strrowid[51],strrowidn[maxcount][51];
      // 把数据的rowid从srctname表中查出来
      sqlstatement selstmt(&conn);
      selstmt.prepare("select rowid from %s %s",srctname,where);
      selstmt.bindout(1, strrowid,50);
      if (selstmt.execute() != 0)
      {
        logfile.Write("%s failed.\n%s\n",selstmt.m_sql,selstmt.m_cda.message); return false;
      }
      // 生成插入dsttname表和删除srctname表的SQL
      int ii=0;
      char strInsertSQL[10241],strDeleteSQL[10241];
      memset(strInsertSQL,0,sizeof(strInsertSQL));
      memset(strDeleteSQL,0,sizeof(strDeleteSQL));
      sprintf(strInsertSQL,"insert into %s(%s) select %s from %s where rowid in (",dsttname,strColumnStr,strColumnStr,srctname);
      //%s(%s)里面的%s是字段名,字段顺序没必要保持一致,所有把列名从数据字典取出,目的表字段比原表少,只备份一部分字段
      sprintf(strDeleteSQL,"delete from %s where rowid in (",srctname);
      
      char strtemp[11];
      for (ii=0; ii<maxcount; ii++)
      {
        memset(strtemp,0,sizeof(strtemp));
        if (ii==0) sprintf(strtemp,":%d",ii+1);
        if (ii >0) sprintf(strtemp,",:%d",ii+1);
        strcat(strInsertSQL,strtemp);
        strcat(strDeleteSQL,strtemp);
      }
      strcat(strInsertSQL,")");
      strcat(strDeleteSQL,")");
    
      sqlstatement insstmt(&conn);
      insstmt.prepare(strInsertSQL);
      sqlstatement delstmt(&conn);
      delstmt.prepare(strDeleteSQL);
    
      for (ii=0; ii<maxcount; ii++)
      {
        insstmt.bindin(ii+1,strrowidn[ii],50);
        delstmt.bindin(ii+1,strrowidn[ii],50);
      }
    
      // 每maxcount记录就执行一次
      while (true)
      {
        memset(strrowid,0,sizeof(strrowid));
        if (selstmt.next() != 0) break;
        strcpy(strrowidn[ccount],strrowid);
        ccount++;
        if (ccount == maxcount)
        {
          if (insstmt.execute() != 0)
          {
            if (insstmt.m_cda.rc != 1)
            {
              logfile.Write("_hsmtables insert %s failed.\n%s\n",dsttname,insstmt.m_cda.message); return false;
            }
          }
          if (delstmt.execute() != 0)
          {
            logfile.Write("_hsmtables delete %s failed.\n%s\n",dsttname,insstmt.m_cda.message); return false;
          }
          conn.commit();
          memset(strrowidn,0,sizeof(strrowidn));
          ccount=0;
        }
    
        if (fmod(selstmt.m_cda.rpc,10000) < 1)
        {
          logfile.Write("%s to %s ok(%d).\n",srctname,dsttname,selstmt.m_cda.rpc);
          // 判断当前时间是否在启动时间之内
          memset(localhour,0,sizeof(localhour));
          LocalTime(localhour,"hh24");
          if (strstr(hourstr,localhour)==0) return true;
        }
      }
      // 在以上循环处理的时候,如果不足maxcount,就在这里处理
      for (ii=0; ii<ccount; ii++)
      {
        insstmt.prepare("\
          BEGIN\
            insert into %s(%s) select %s from %s where rowid=:1;\
            delete from %s where rowid=:2;\
          END;",dsttname,strColumnStr,strColumnStr,srctname,srctname);
        insstmt.bindin(1,strrowidn[ii],50);
        insstmt.bindin(2,strrowidn[ii],50);
        if (insstmt.execute() != 0)
        {
          if (insstmt.m_cda.rc != 1)
          {
            logfile.Write("_hsmtables insert %s or delete %s failed.\n%s\n",dsttname,srctname,insstmt.m_cda.message); return false;
          }
        }
      }
      conn.commit(); //一行一行的提交
      logfile.Write("%s to %s finish(%d).\n",srctname,dsttname,selstmt.m_cda.rpc);
      return true;
    }
    
    ///////////////////////////////////获取dsttname表全部的列
    bool GetColumnStr()
    {
      memset(strColumnStr,0,sizeof(strColumnStr)); 
      char column_name[51];
      sqlstatement stmt(&conn);
      stmt.prepare("select lower(column_name) from USER_TAB_COLUMNS where table_name=upper('%s') order by column_id",dsttname);
      stmt.bindout(1,column_name,50);
      if (stmt.execute() != 0)
      {
        logfile.Write("%s failed.\n%s\n",stmt.m_sql,stmt.m_cda.message); return false;
      }
      while(true)
      {
        memset(column_name,0,sizeof(column_name));
        if (stmt.next()!=0) break;
        if (stmt.m_cda.rpc>1) strcat(strColumnStr,",");
        strcat(strColumnStr,column_name);
      }
      if (stmt.m_cda.rpc==0) { logfile.Write("表%s不存在。\n",dsttname); return false; }
      return true;
    }
    
    在这里插入图片描述

    $sqlplus shqx/pwdidc,如下数据都是一小时前生成,需要将后台生成数据脚本启动


    在这里插入图片描述

    如下是数据迁移程序如何兼容其他数据库,oracle有rowid,mysql和pg都没有rowid但有keyid,取列名时每个数据库的数据字典都不同


    在这里插入图片描述
    不管id字段是数字还是字符或字符串,我们都可用字符绑定
    在这里插入图片描述
    如下不同数据库查数据字典得到全部列
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    5.监控告警子系统:分区,mount,fdisk

    我们处理后台数据也会有一个web系统,管理参数,展示数据,查询数据等界面(不能让用户去登录plsqldeveloper查询)


    在这里插入图片描述

    如下是数据的监控,数据少于某数量显示红色


    在这里插入图片描述
    如下系统告警,监控参数和日志
    在这里插入图片描述

    数据有延时,统计每隔时次如全国气象站表数据量多少,再用其他程序分析下数据大报是否正常


    在这里插入图片描述
    在这里插入图片描述
    以下是怎么分区安装centos,df -h 显示G。如下日志等等都在sda中即raid1即2个300G中
    h
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    以下是收集磁盘空间信息,现在插入一个U盘,fdisk -l可以看到U盘。df可看到%使用率
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    每个服务器上运行一个收集磁盘空间的小程序,收集到磁盘空间后生成xml文件存放在本地目录,通过文件传输系统或ftp将文件传给数据处理服务器(因为有的服务器不安装oracle客户端,只可安装文件传输客户端),统一保存到数据库。


    在这里插入图片描述
    //diskinfo.cpp,写入xml文件中再入库
    #include "_public.h"
    void EXIT(int sig);
    CLogFile logfile;
    
    int main(int argc,char *argv[])
    {
      if (argc != 4)
      {
        printf("\n");
        printf("Using:./diskinfo hostname logfilename outputpath\n");
    
        printf("Example:/htidc/public/bin/diskinfo 118.89.50.198 /tmp/htidc/log/diskinfo.log /tmp/htidc/monclient\n\n");
        printf("此程序调用df命名,把本服务器的磁盘使用率信息写入xml文件。\n");
        printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
        printf("logfilename是本程序的日志文件名。\n");
        printf("outputpath是输出的xml文件存放的目录。\n");
        printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");
    
        return -1;
      }
    
      // 关闭全部的信号和输入输出
      // 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
      // 但请不要用 "kill -9 +进程号" 强行终止
      CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
    
      if (logfile.Open(argv[2],"a+") == false)
      {
        printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
      }
    
      FILE *fp=0;
    
      if ( (fp=popen("df -k --block-size=1M","r")) == NULL )
      {
        logfile.Write("popen(df -k --block-size=1M) failed.\n"); return false;
      }
    
      char strXMLFileName[301],strLocalTime[21];
      memset(strXMLFileName,0,sizeof(strXMLFileName));
      memset(strLocalTime,0,sizeof(strLocalTime));
      LocalTime(strLocalTime,"yyyymmddhh24miss");
      snprintf(strXMLFileName,300,"%s/diskinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
    
      CFile XMLFile;
      if (XMLFile.OpenForRename(strXMLFileName,"w+") == false )
      {
        logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); pclose(fp); return -1;
      }
    
      XMLFile.Fprintf("<data>\n");
    
      CCmdStr CmdStr;
      char strBuffer[1024],strLine[500];
      
      while (true)
      {
        memset(strBuffer,0,sizeof(strBuffer));
    
        if (FGETS(fp,strBuffer,500) == false) break;
    
        // 如果没有找到“%”,就再读取一行,与strBuffer拼起来
        if (strstr(strBuffer,"%") == 0)
        {
          memset(strLine,0,sizeof(strLine));
          if (FGETS(fp,strLine,500) == false) break;
          strcat(strBuffer," "); strcat(strBuffer,strLine);
        }
    
        // 删除字符串前后的空格和换行符
        DeleteLRChar(strBuffer,' '); DeleteLRChar(strBuffer,'\n');
    
        // 把字符串中间的多个空格全部转换为一个空格
        UpdateStr(strBuffer,"  "," ");
    
        // 把全内容全部转换为小写
        ToLower(strBuffer);
    
        // 除了磁盘信息,还有可能是内存,SMB等其它文件,都丢弃掉
        if (strncmp(strBuffer,"/dev",4) != 0) continue;
    
        CmdStr.SplitToCmd(strBuffer," ");
    
        if (CmdStr.CmdCount() != 6) continue;
    
        char strusep[21];
        memset(strusep,0,sizeof(strusep));
        strcpy(strusep,CmdStr.m_vCmdStr[4].c_str());
        UpdateStr(strusep,"%","");
    
        char strLocalTime[21];
        memset(strLocalTime,0,sizeof(strLocalTime));
        LocalTime(strLocalTime,"yyyymmddhh24miss");
        XMLFile.Fprintf(\
                "<nodip>%s</nodip>"\
                "<crttime>%s</crttime>"\
                "<filesystem>%s</filesystem>"\
                "<total>%0.02f</total>"\
                "<used>%0.02f</used>"\
                "<available>%0.02f</available>"\
                "<usep>%0.02f</usep>"\
                "<mount>%s</mount><endl/>\n",
                 argv[1],
                 strLocalTime,
                 CmdStr.m_vCmdStr[0].c_str(),
                 atof(CmdStr.m_vCmdStr[1].c_str())/1024.0,
                 atof(CmdStr.m_vCmdStr[2].c_str())/1024.0,
                 atof(CmdStr.m_vCmdStr[3].c_str())/1024.0,
                 (atof(CmdStr.m_vCmdStr[2].c_str())/atof(CmdStr.m_vCmdStr[1].c_str()))*100.0,
                 CmdStr.m_vCmdStr[5].c_str());
      }
    
      XMLFile.Fprintf("</data>\n");
    
      pclose(fp);
    
      XMLFile.CloseAndRename();
    
      logfile.Write("create %s ok.\n",strXMLFileName);
    
      exit(0);
    }
    
    void EXIT(int sig)
    {
      if (sig > 0) signal(sig,SIG_IGN);
      logfile.Write("catching the signal(%d).\n",sig);
      logfile.Write("diskinfo exit.\n");
      exit(0);
    }
    

    以下为收集CPU和内存信息,top命令显示如下,zombie表示僵尸,q退出,cpu用到20%算忙了

    在这里插入图片描述
    ps -ef |grep htidc,cpuinfo.cpp思路是定义一个数据结构,三个结构体变量,加载cpu信息到结构体里,睡60s,再继续加载cpu信息到结构体里,再将两结构体成员相减,就可以知道一分钟内cpu情况,采用的是一分钟信息。vi /proc/stat如下
    在这里插入图片描述
    //cpuinfo.cpp
    #include "_public.h"
    void EXIT(int sig);
    CLogFile logfile;
    struct st_cpuinfo
    {
      double user;
      double sys;
      double wait;
      double nice;
      double idle;
      double irq;
      double softirq;
      double total;
    };
    struct st_cpuinfo stcpuinfo1,stcpuinfo2,stcpuinfo3;
    bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo);
    
    int main(int argc,char *argv[])
    {
      if (argc != 4)
      {
        printf("\n");
        printf("Using:./cpuinfo hostname logfilename outputpath\n");
        printf("Example:/htidc/public/bin/cpuinfo 118.89.50.198 /tmp/htidc/log/cpuinfo.log /tmp/htidc/monclient\n\n");
    
        printf("此程序读取/proc/stat文件,把本服务器的CPU使用率信息写入xml文件。\n");
        printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
        printf("logfilename是本程序的日志文件名。\n");
        printf("outputpath是输出的xml文件存放的目录。\n");
        printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");
     
        return -1;
      }
      //memset(strHostName,0,sizeof(strHostName));
      //strncpy(strHostName,argv[2],20);
      // 关闭全部的信号和输入输出
      // 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
      // 但请不要用 "kill -9 +进程号" 强行终止
      CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
      if (logfile.Open(argv[2],"a+") == false)
      {
        printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
      }
      memset(&stcpuinfo1,0,sizeof(struct st_cpuinfo));
      memset(&stcpuinfo2,0,sizeof(struct st_cpuinfo));
      memset(&stcpuinfo3,0,sizeof(struct st_cpuinfo));
    
      if (LoadCPUInfo(stcpuinfo1) ==false) return -1;  
      sleep(60);
      if (LoadCPUInfo(stcpuinfo2) ==false) return -1;
    
      stcpuinfo3.user=stcpuinfo2.user-stcpuinfo1.user;
      stcpuinfo3.sys=stcpuinfo2.sys-stcpuinfo1.sys;
      stcpuinfo3.wait=stcpuinfo2.wait-stcpuinfo1.wait;
      stcpuinfo3.nice=stcpuinfo2.nice-stcpuinfo1.nice;
      stcpuinfo3.idle=stcpuinfo2.idle-stcpuinfo1.idle;
      stcpuinfo3.irq=stcpuinfo2.irq-stcpuinfo1.irq;
      stcpuinfo3.softirq=stcpuinfo2.softirq-stcpuinfo1.softirq;
    
      stcpuinfo3.total=stcpuinfo3.user+stcpuinfo3.sys+stcpuinfo3.wait+stcpuinfo3.nice+stcpuinfo3.idle+stcpuinfo3.irq+stcpuinfo3.softirq;
    
      char strLocalTime[21];
      memset(strLocalTime,0,sizeof(strLocalTime));
      LocalTime(strLocalTime,"yyyymmddhh24miss");
    
      char strXMLFileName[301];
      memset(strXMLFileName,0,sizeof(strXMLFileName));
      snprintf(strXMLFileName,300,"%s/cpuinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
    
      CFile XMLFile;
      if (XMLFile.OpenForRename(strXMLFileName,"w+") == false )
      {
        logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); return -1;
      }
      XMLFile.Fprintf("<data>\n");
    
      XMLFile.Fprintf("<nodip>%s</nodip><crttime>%s</crttime><user>%0.02f</user><sys>%0.02f</sys><wait>%0.02f</wait><nice>%0.02f</nice><idle>%0.02f</idle><usep>%0.02f</usep><endl/>\n",argv[1],strLocalTime,stcpuinfo3.user/stcpuinfo3.total*100.0,stcpuinfo3.sys/stcpuinfo3.total*100.0,stcpuinfo3.wait/stcpuinfo3.total*100.0,stcpuinfo3.nice/stcpuinfo3.total*100.0,stcpuinfo3.idle/stcpuinfo3.total*100.0,100.0-stcpuinfo3.nice/stcpuinfo3.total*100.0);
    
      XMLFile.Fprintf("</data>\n");
      XMLFile.CloseAndRename();
      logfile.Write("create %s ok.\n",strXMLFileName);
      exit(0);
    }
    void EXIT(int sig)
    {
      if (sig > 0) signal(sig,SIG_IGN);
      logfile.Write("catching the signal(%d).\n",sig);
      logfile.Write("cpuinfo exit.\n");
      exit(0);
    }
    
    bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo)
    {
      CFile CPUFile;
      if (CPUFile.Open("/proc/stat","r") == false )
      {
        logfile.Write("CPUFile.OpenForRead(/proc/stat) failed.\n"); return false;
      }
    
      CCmdStr CmdStr;
      char strBuffer[1024];
      while (true)
      {
        memset(strBuffer,0,sizeof(strBuffer));
        if (CPUFile.FFGETS(strBuffer,500) == false) break;
        // 删除字符串前后的空格
        DeleteLRChar(strBuffer,' ');
        // 把字符串中间的多个空格全部转换为一个空格
        UpdateStr(strBuffer,"  "," ");
        ToLower(strBuffer);
        CmdStr.SplitToCmd(strBuffer," ");
        if (strcmp(CmdStr.m_vCmdStr[0].c_str(),"cpu")==0) 
        {
          stcpuinfo.user=atof(CmdStr.m_vCmdStr[1].c_str());
          stcpuinfo.sys=atof(CmdStr.m_vCmdStr[2].c_str());
          stcpuinfo.wait=atof(CmdStr.m_vCmdStr[3].c_str());
          stcpuinfo.nice=atof(CmdStr.m_vCmdStr[4].c_str());
          stcpuinfo.idle=atof(CmdStr.m_vCmdStr[5].c_str());
          stcpuinfo.irq=atof(CmdStr.m_vCmdStr[6].c_str());
          stcpuinfo.softirq=atof(CmdStr.m_vCmdStr[7].c_str());
          return true;
        }
      }
      logfile.Write("Read /proc/stat failed.\n"); 
      return false;
    }
    
    在这里插入图片描述

    vi /tmp/htdic/monclient/cpu*,如下是收集到的信息


    在这里插入图片描述

    收集内存信息#free -m,和top命令查看的内存是一样的,也在系统文件vi /proc/meminfo


    在这里插入图片描述
    以下是收集Oracle表空间信息,表空间就像磁盘空间一样,表空间的信息收集要去读取oracle的数据字典,如下sql是查询oracle表空间使用率,取出后写入xml文件里,vi tbspaceinfo.cpp,要dba权限。
    select * from (
    Select a.tablespace_name,
    to_char(a.bytes/1024/1024,'99,999.999') total_bytes,
    to_char(b.bytes/1024/1024,'99,999.999') free_bytes,
    to_char(a.bytes/1024/1024 - b.bytes/1024/1024,'99,999.999') use_bytes,
    to_char((1 - b.bytes/a.bytes)*100,'99.99') || '%' use
    from (select tablespace_name,
    sum(bytes) bytes
    from dba_data_files
    group by tablespace_name) a,
    (select tablespace_name,
    sum(bytes) bytes
    from dba_free_space
    group by tablespace_name) b
    where a.tablespace_name = b.tablespace_name
    union all
    select c.tablespace_name,
    to_char(c.bytes/1024/1024,'99,999.999') total_bytes,
    to_char( (c.bytes-d.bytes_used)/1024/1024,'99,999.999') free_bytes,
    to_char(d.bytes_used/1024/1024,'99,999.999') use_bytes,
    to_char(d.bytes_used*100/c.bytes,'99.99') || '%' use
    from
    (select tablespace_name,sum(bytes) bytes
    from dba_temp_files group by tablespace_name) c,
    (select tablespace_name,sum(bytes_cached) bytes_used
    from v$temp_extent_pool group by tablespace_name) d
    where c.tablespace_name = d.tablespace_name
    )
    
    在这里插入图片描述
    //tbspaceinfo.cpp
    #include "_public.h"
    #include "_ooci.h"
    void EXIT(int sig);
    struct st_TBSPACEINFO
    {
      long  taskid;
      char  nodip[31];
      char  tablespace[101];
      double  total;
      double  used;
      double  available;
      double  usep;
      double alarmvalue;
      int  alarmsts;
      char crttime[21];
      int  rsts;
    };
    struct st_TBSPACEINFO stTBSPACEINFO;
    CLogFile logfile;
    connection conn;
    int main(int argc,char *argv[])
    {
      if (argc != 5)
      {
        printf("\n");
        printf("Using:./tbspaceinfo hostname logfilename outputpath username/password@tnsnames\n");
    
        printf("Example:/htidc/public/bin/tbspaceinfo 10.153.98.13 /tmp/htidc/log/tbspaceinfo_10.153.98.13.log /tmp/htidc/monclient shqx/pwdidc@SZQX_10.153.98.13\n\n");
        printf("此程序连接远程数据库,把远程数据库表空间使用率信息写入xml文件。\n");
        printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
        printf("logfilename是本程序的日志文件名。\n");
        printf("outputpath是输出的xml文件存放的目录。\n");
        printf("username/password@tnsnames为待监控的远程数据库的用户名/密码@连接名。\n");
        printf("此程序运行在数据中心应用程序的服务器上。\n\n\n");
    
        return -1;
      }
      // 关闭全部的信号和输入输出
      // 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
      // 但请不要用 "kill -9 +进程号" 强行终止
      CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
      if (logfile.Open(argv[2],"a+") == false)
      {
        printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
      }
      if (conn.connecttodb(argv[4],"Simplified Chinese_China.ZHS16GBK") != 0)
      {
        logfile.Write("conn.connecttodb(%s) failed.\n",argv[4]); return -1;
      }
      
      sqlstatement stmt;
      stmt.connect(&conn);
      stmt.prepare("\
        select f.tablespace_name,a.total,u.used,f.free,(u.used/a.total)*100  from\
            (select tablespace_name,sum(bytes/(1024*1024*1024)) total from DBA_DATA_FILES\
              group by tablespace_name) a,\
            (select tablespace_name,round(sum(bytes/(1024*1024*1024))) used from DBA_EXTENTS\
              group by tablespace_name) u,\
            (select tablespace_name,round(sum(bytes/(1024*1024*1024))) free from DBA_FREE_SPACE\
              group by tablespace_name) f\
        where a.tablespace_name = f.tablespace_name\
          and a.tablespace_name = u.tablespace_name\
          and f.tablespace_name in (select tablespace_name from DBA_TABLESPACES where contents='PERMANENT')");
      stmt.bindout(1,stTBSPACEINFO.tablespace,100);
      stmt.bindout(2,&stTBSPACEINFO.total);
      stmt.bindout(3,&stTBSPACEINFO.used);
      stmt.bindout(4,&stTBSPACEINFO.available);
      stmt.bindout(5,&stTBSPACEINFO.usep);
    
      if (stmt.execute() != 0)
      {
        logfile.Write("select DBA_DATA_FILES,DBA_EXTENTS,DBA_FREE_SPACE failed.\n%s\n",stmt.m_cda.message); return -1;
      }
    
      char strLocalTime[21];
      memset(strLocalTime,0,sizeof(strLocalTime));
      LocalTime(strLocalTime,"yyyymmddhh24miss");
    
      char strXMLFileName[301];
      memset(strXMLFileName,0,sizeof(strXMLFileName));
      snprintf(strXMLFileName,300,"%s/tbspaceinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
    
      CFile XMLFile;
      if (XMLFile.OpenForRename(strXMLFileName,"w+") == false )
      {
        logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); return -1;
      }
      XMLFile.Fprintf("<data>\n");
    
      while (true)
      {
        memset(&stTBSPACEINFO,0,sizeof(stTBSPACEINFO));
        if (stmt.next() != 0) break;
        XMLFile.Fprintf(\
                "<nodip>%s</nodip>"\
                "<crttime>%s</crttime>"\
                "<tablespace>%s</tablespace>"\
                "<total>%0.02f</total>"\
                "<used>%0.02f</used>"\
                "<available>%0.02f</available>"\
                "<usep>%0.02f</usep><endl/>\n",
                 argv[1],
                 strLocalTime,
                 stTBSPACEINFO.tablespace,
                 stTBSPACEINFO.total,
                 stTBSPACEINFO.used,
                 stTBSPACEINFO.available,
                 stTBSPACEINFO.usep);
      }
      XMLFile.Fprintf("</data>\n");
      XMLFile.CloseAndRename();
      logfile.Write("生成文件%s.\n",strXMLFileName);
      exit(0);
    }
    void EXIT(int sig)
    {
      if (sig > 0) signal(sig,SIG_IGN);
      logfile.Write("catching the signal(%d).\n",sig);
      logfile.Write("tbspaceinfo exit.\n");
      exit(0);
    }
    
    在这里插入图片描述
    为表空间增加数据文件有两种方式:固定大小和自动增长(自动增长对监控意义不大,使用率永远98%,99%)
    以下是收集Oracle会话信息,我们用客户端通oracle的监听连上oracle数据库,oracle数据库会启动一个进程向会话提供服务,是多进程的服务端,每增加一连接(进程),需要消耗系统资源(内存,socket连接)。如下一共有6个客户端连上,SQL>exit退出一个就剩下5个。ps -ef |grep oracle可查看oracle数据库系统进程。
    在这里插入图片描述
    在这里插入图片描述
    如下两个sql是查询数据字典查到进程和上面一样,第一个sql是查全部包括系统进程。如下表有一个OSUSER表示连上来的客户端操作系统名字,还可以通过SQL_ID查到连上来的客户端sql语句操作。还可以指定oracle用户的会话数
    在这里插入图片描述
    在这里插入图片描述
    // dbsessioninfo.cpp
    #include "qxmon.h"
    void EXIT(int sig);
    struct st_DBSESSIONINFO stDBSESSIONINFO;
    
    CLogFile logfile;
    connection conndst;
    CProgramActive ProgramActive;
    
    int main(int argc,char *argv[])
    {
      if (argc != 5)
      {
        printf("\n");
        printf("Using:./dbsessioninfo hostname logfilename outputpath username/password@tnsnames\n");
    
        printf("Example:/htidc/htidc/bin/procctl 300 /htidc/qxmon/bin/dbsessioninfo 10.153.98.13 /log/qxmon/dbsessioninfo_10.153.98.13.log /qxdata/qxmon/qxmonclient szidc/pwdidc@SZQX_10.153.98.13\n\n");
        printf("此程序连接远程数据库,把远程数据库会话信息写入XML文件。\n");
        printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
        printf("logfilename是本程序的日志文件名。\n");
        printf("outputpath是输出的XML文件存放的目录。\n");
        printf("username/password@tnsnames为待监控的远程数据库的用户名/密码@连接名。\n");
        printf("此程序运行在监控平台的服务器上。\n\n\n");
    
        return -1;
      }
    
      // 关闭全部的信号和输入输出
      // 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
      // 但请不要用 "kill -9 +进程号" 强行终止
      CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
    
      if (logfile.Open(argv[2],"a+") == FALSE)
      {
        printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
      }
    
      ProgramActive.SetProgramInfo(&logfile,"alarmserver",300);
    
      if (conndst.connecttodb(argv[4])!=0)
      {
        logfile.Write("connmon.connecttodb(%s) failed.\n",argv[4]); EXIT(-1);
      }
    
      sqlstatement stmt;
      stmt.connect(&conndst);
      stmt.prepare("select count(*) from V$SESSION where type!='BACKGROUND' and status!='KILLED'");
      stmt.bindout(1,&stDBSESSIONINFO.total);
    
      if (stmt.execute() != 0)
      {
        logfile.Write("select V$SESSION failed.\n%s\n",stmt.cda.message); EXIT(-1);
      }
    
      char strLocalTime[21];
      memset(strLocalTime,0,sizeof(strLocalTime));
      LocalTime(strLocalTime,"yyyymmddhh24miss");
    
      char strXMLFileName[301];
      memset(strXMLFileName,0,sizeof(strXMLFileName));
      snprintf(strXMLFileName,300,"%s/dbsessioninfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
    
      CFile XMLFile;
      if (XMLFile.OpenForRename(strXMLFileName,"w+") == FALSE )
      {
        logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); EXIT(-1);
      }
    
      XMLFile.Fprintf("<data>\n");
    
      while (TRUE)
      {
        memset(&stDBSESSIONINFO,0,sizeof(stDBSESSIONINFO));
    
        if (stmt.next() != 0) break;
    
        XMLFile.Fprintf(\
                "<nodip>%s</nodip>"\
                "<crttime>%s</crttime>"\
                "<total>%d</total><endl/>\n",
                 argv[1],
                 strLocalTime,
                 stDBSESSIONINFO.total);
      }
    
      XMLFile.Fprintf("</data>\n");
    
      XMLFile.CloseAndRename();
    
      logfile.Write("生成文件%s.\n",strXMLFileName);
    
      exit(0);
    }
    
    void EXIT(int sig)
    {
      if (sig > 0) signal(sig,SIG_IGN);
      logfile.Write("catching the signal(%d).\n",sig);
      logfile.Write("dbsessioninfo exit.\n");
      exit(0);
    }
    
    在这里插入图片描述
    oracle里有一个系统参数processes(最大允许的会话总数)。_ooci.h中connection类连接上一个后进程及processes+1,sqlstatement类实例化并指定数据库连接也会消耗一种资源即open_cursors参数。oracle有缓存机制存sql,下次用到则不进行语法分析,所以oracle需大内存,存的越多,缓存命中率越高。
    在这里插入图片描述
    收集到了信息放入oracle表,再写一个程序去判断是否告警。告警了的话形成一段文字如哪个服务器ip的cpu使用率超了多少或哪个分区磁盘空间超了多少。告警信息通知维护人员两个方案实现:1.将手机号和短信内容通过接口(数据库表这种接口,http,文件)给短信平台。2.无短信平台,写一个程序分析收到的信息,形成一段文字,调用邮件功能(web服务器:tomcat,java发邮件)

    相关文章

      网友评论

          本文标题:【C/C++】项目_8_数据挖掘/HTTP协议/非结构化数据存储

          本文链接:https://www.haomeiwen.com/subject/mfcjhktx.html