模拟搭建日志收集系统

Hadoop-模拟搭建日志收集系统

一、技术点梳理

  • Nginx:是一个web server,主要做反向代理,起到分发用户请求的作用,在集群环境时,也可以负载均衡
  • Spawn cgi:提供一个cgi网关接口,可以将server服务快速暴露出去,以便对外提供服务。走cgi协议,该协议是一种特殊的http请求(一般的http请求安全性相对较差,容易受到外部攻击)
  • Thrift RPC:通过执行thrift命令,可以快速生成client和server 代码,同时由于thrift rpc是跨语言的,只要同时遵循client和server端通信的rpc协议和接口规范,两端可以使用不同的语言来进行交互。当生产中,使用c++或java进行开发时,可以大大提高冰并发请求的性能
  • ab压测:用来模拟大量用户并发请求,模拟高并发场景下的所有请求的平均响应时间
  • Glog:谷歌的一个开源日志框架,可以实现快速的写入log日志文件,并可以指定文件存放的位置,单个文件的大小,分割log日志文件的周期等,功能类似java的log4j
  • Flume:作为一个通道对接log server产生的log文件,使其经过source,channel,最后到达自己重新定义的HBaseSink,重新定义是为了将非结构的日志数据转换成结构化的数据,分别存储在表中的不同列里面,而HBaseSink原来的sink只能帮助我们制定不同的列
  • Hbase:存储经过结构化处理的用户行为信息,后期可以使用HIve对HBase中的数据进行统计和分析

二、任务

当前环境:

python:python2.7
java:1.8.0_171
操作系统:centos7

2.1 调通单机版的thrift(python版本)

2.1.1 安装thrift

1、下载源码包

wget http://apache.fayea.com/thrift/0.9.3/thrift-0.9.3.tar.gz  

2、解压源码包

tar xzvf thrift-0.9.3.tar.gz 

3、 安装依赖条件
thrift核心代码使用c++编写,用了boost库.执行以下命令,安装相关依赖:

yum install automake libtool flex bison pkgconfig gcc-c++ boost-devel libevent-devel zlib-devel python-devel ruby-devel

4、 检测安装平台的目标特征
进入解压之后的文件夹,配置支持与不支持的语言

./configure --with-cpp --with-boost --with-python --without-csharp --with-java --without-erlang --without-perl --with-php --without-php_extension --without-ruby --without-haskell  --without-go  

执行命令,可能会报出如下错误信息:

configure: error: "Error: libcrypto required."  

解决方法:安装 openssl openssl-devel,执行命令如下:

yum -y install openssl openssl-devel  

5、编译&安装

编译:make(编译前要安装g++)
安装:make install

6、检测thrift是否安装成功

查看thrift是否安装成功:thrift、thrift -help
查看thrift的安装路径:which thrift

2.1.2 定义client和server通信的接口

定义通信的schema,执行thrift做准备,创建RecSys.thrift文件

touch RecSys.thrift

文件内容如下:

service RecSys {
    string rec_data(1:string data)
}

2.1.3 根据接口(scheme)生成python代码

thrift --gen py RecSys.thrift

执行过程可能报错:No module named thrift.transport
解决方法:

pip install thrift==0.9.3

2.1.4 client端和server端代码

查看client端代码:

cat client.py
#! /usr/bin/env python  
# -*- coding: utf-8 -*-  

import sys  
#追加目录,识别对应的库  
sys.path.append("gen-py")  

from thrift import Thrift  
from thrift.transport import TSocket  
from thrift.transport import TTransport  
from thrift.protocol import TBinaryProtocol  

from RecSys import RecSys  
# from demo.ttypes import *  

try:  
    # Make Socket  
    # 建立socket, IP 和port要写对  
    transport = TSocket.TSocket('localhost', 9900)  

    # Buffering is critical. Raw sockets are very slow  
    # 选择传输层,这块要和服务器的设置一样  
    transport = TTransport.TBufferedTransport(transport)  

    # Wrap in a protocol  
    # 选择传输协议,这个也要和服务器保持一致,负责无法通信  
    protocol = TBinaryProtocol.TBinaryProtocol(transport)  

    client = RecSys.Client(protocol)  

    # Connect!  
    transport.open()  

    # Call server services  
    rst = client.rec_data("are you ok!")  
    print rst  

    # close transport  
    transport.close()  
except Thrift.TException, ex:  
    print "%s" % (ex.message)  

server端代码:

cat server.py
#! /usr/bin/env python  
# -*- coding: utf-8 -*-  

import sys  
sys.path.append('gen-py')  

from thrift.transport import TSocket  
from thrift.transport import TTransport  
from thrift.protocol import TBinaryProtocol  
from thrift.server import TServer  

from RecSys import RecSys  
from RecSys.ttypes import *  


class RecSysHandler(RecSys.Iface):  
    def rec_data(self, a):  
        print "Receive: %s" %(a)  
        return "ok"  


if __name__ == "__main__":  

    # 实例化handler  
    handler = RecSysHandler()  

    # 设置processor  
    processor = RecSys.Processor(handler)  

    # 设置端口  
    transport = TSocket.TServerSocket('localhost', port=9900)  

    # 设置传输层  
    tfactory = TTransport.TBufferedTransportFactory()  

    # 设置传输协议  
    pfactory = TBinaryProtocol.TBinaryProtocolFactory()  

    server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)  

    print 'Starting the server...'  
    server.serve()  
    print 'done'  

2.1.5 启动测试

先启动server,后启动client

python server.py
python client.py

server端结果:

Receive: are you ok!

client端结果:

ok

完整了最基本的C/S架构(python)

2.2 调通单机版的thrift(c++版本)

c++版本的thrift是帮我们仅仅生成了server端,之所以要用c++版本,是因为c++性能更高,并发效果更好,生产中是最常用的.

2.2.1 根据接口生成C++的server端代码

thrift --gen cpp RecSys.thrift 

会生成一个gen-cpp目录,其中的RecSys_server.skeleton.cpp 是server端,只需要修改rec_data函数中的内容即可:

void rec_data(std::string& _return, const std::string& data) {
      // Your implementation goes here
      printf("rec_data\n");
    }

2.2.2 相关c++包的配置

1、编译之前要安装一个包,否则编译可能会通不过,已安装的话,请忽略:

yum install boost-devel-static  

2、复制lib包,统一管理,进入这个目录

thrift-0.9.3/lib/cpp/src

lib包的复制

cp -raf ./thrift/ /usr/local/include/  

2.2.3 编译

编译server,执行thrift命令之后,会生成一个gen-cpp的文件夹,进入执行以下命令,分别进行编译:

g++ -g -Wall -I./ -I/usr/local/include/thrift RecSys.cpp RecSys_constants.cpp RecSys_types.cpp RecSys_server.skeleton.cpp -L/usr/local/lib/*.so -lthrift -o server 

client端代码需要我们自己手写,代码实例如下:

#include "RecSys.h"
#include <iostream>
#include <string>


#include <transport/TSocket.h>
#include <transport/TBufferTransports.h>
#include <protocol/TBinaryProtocol.h>


using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace std;
using std::string;
using boost::shared_ptr;

int main(int argc, char **argv) {

    boost::shared_ptr<TSocket> socket(new TSocket("localhost", 9090));
    boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

    transport->open();

    RecSysClient client(protocol);

    string send_data = "are you ok?";
    string receive_data;

    client.rec_data(receive_data, send_data);
    cout << "receive server data: " << receive_data << endl;

    transport->close();
    return 0;
}

编译client:

g++ -g -Wall -I./ -I/usr/local/include/thrift RecSys.cpp client.cpp -L/usr/local/lib/*.so -lthrift -o client  

2.2.4 运行

运行server

./server  

运行client:

./client  

2.3 实现thrift多语言互通

Thrift RPC跨语言,只要client和server端遵循通信的rpc协议和接口规范,两端完全可以使用不同的语言进行开发.
server端代码要进行局部修改,以便更好的进行测试:

client:python
server:c++

2.3.1 执行thrift命令,生成server端代码

thrift --gen cpp RecSys.thrift 

自动生成gen-cpp目录,这个目录下的代码仅完成server功能

2.3.2 在server端代码进行如下内容的修改:

void rec_data(std::string& _return, const std::string& data) {  
     printf("==============\n");  
     std::cout << "receive client data: " << data << std::endl;  

     std::string ack = "i am ok !!!";  
     _return = ack;  

   }

2.3.3 client端代码仍使用任务1中的Python代码:

只需要对应server端,修改端口号即可.

2.3.4 运行

运行server

./server  

运行client

python client.py  

server端结果:

===================
receive cleint data: are you ok!

client结果:

i am ok !!!

2.4 搭建nginx服务器

2.4.1 下载nginx

wget http://nginx.org/download/nginx-1.14.0.tar.gz  

2.4.2 解压安装

tar xvzf nginx-1.14.0.tar.gz   

2.4.3 配置安装路径

解压后,进入文件夹nginx-1.14.0中,执行如下配置,并执行安装路径:

./configure --prefix=/usr/local/nginx  

若安装出错,可尝试安装如下依赖包:

yum -y install pcre-devel zlib-devel  

2.4.4 编译

进入安装路径/usr/local/nginx-1.14.0中,执行如下命令,进行编译:

make
make install

2.4.5 启动访问

在/usr/local/nginx/sbin目录下,执行:

nginx

在浏览器中输入安装的nginx所在机器的ip,出现以下提示,表示nginx安装成功:
这里写图片描述
查看一下运行的端口

netstat -antup | grep -w 80  
ps aux | grep nginx  

kill掉线程

killall -9 nginx  

2.5 配合Spawn cgi完成独立server

通过cgi提供的网关接口,可以将自己的server服务提供给外部,供外部用户进行访问请求.可以理解为提供了一种代理,可以在非应用程序所在的机器上操作应用程序,并对应用程序发送请求.

2.5.1 下载cgi并安装

下载并解压:

wget http://download.lighttpd.net/spawn-fcgi/releases-1.6.x/spawn-fcgi-1.6.4.tar.gz 
tar xzvf spawn-fcgi-1.6.4.tar.gz

进入spawn-fcgi-1.6.4目录下,配置、编译并安装:

./configure  
make  
make install

2.5.2 copy bin目录,将所有的bin目录都放在一起

cp src/spawn-fcgi /usr/local/nginx/sbin/ 

2.5.3 下载fcgi并安装

fcgi是cgi应用程序依赖的库.
下载并解压:

wget ftp://ftp.ru.debian.org/gentoo-distfiles/distfiles/fcgi-2.4.1-SNAP-0910052249.tar.gz  
tar xzvf fcgi-2.4.1-SNAP-0910052249.tar.gz  

2.5.4 修改一处代码

find . -name fcgio.h

检索出路径为: ./include/fcgio.h

vim ./include/fcgio.h 

在#include 下添加一个标准输出:

#include <cstdio> 

2.5.5 配置安装三部曲

./configure  
make  
make install 

2.5.6 创建cgi的一个小demo

创建一个test.c的文件

#include <stdio.h>  
#include <stdlib.h>  
#include <fcgi_stdio.h>  

int main() {  

    int count = 0;  
    while(FCGI_Accept() >= 0) {  
        printf("Content-type: text/html\r\n"  
                "\r\n"  
                ""  
                "Hello Badou EveryBody!!!"  
                "Request number %d running on host %s "  
                "Process ID: %d\n ", ++count, getenv("SERVER_NAME"), getpid());  

    }  

    return 0;  
}  

编译代码:

gcc -g -o test test.c -lfcgi  

生成test文件
如果提示找不到lib库,修改ld.so.conf文件

(py27tf) [root@singler cgi_demo]# vim /etc/ld.so.conf 
include ld.so.conf.d/*.conf  
/usr/local/lib  

执行命令,重新加载配置: ldconfig

2.5.7 执行二进制文件并启动cgi

测试

./test

测试成功后,启动spawn cgi进行代理:

/usr/local/nginx/sbin/spawn-fcgi -a 127.0.0.1 -p 8088 -f /home/thrift_test/cgi_demo/test

参数说明:

-f 启动应用文件的存放路径  
-p 启动一个应用程序(常驻进程),对外访问的端口  

检查端口是否正常

netstat -antup |grep 8088

2.5.8 配置nginx的反向代理:

配置nginx,使用户的请求通过nginx反向代理打到通过cgi暴露的server服务上
配置反向代理,nginx.conf文件:

location / {
             root   html;
            index  index.html index.htm;
       }

location ~ /recsys$ {
             fastcgi_pass 127.0.0.1:8088;
             include fastcgi_params;
         }

启动nginx

./sbin/nginx

2.5.9 测试

测试:

http://192.168.153.151/recsys

完成只读型的demo

由于无法接收和解析参数,扩展性不强,需要代码加固和升级:

http://192.168.153.151/recsys?itemid=111&userid=012&action=click&ip=10.11.11.10

2.6 Thrift rpc和Spawn cgi进行联合试验,完成日志服务器

2.6.1 c++的client代码:

在目录/thrift_test_myself/thrift_cgi_demo/gen-cpp下

touch client.cpp
#include "RecSys.h"
#include <iostream>
#include <string>


#include <thrift/transport/TSocket.h>  
#include <thrift/transport/TBufferTransports.h>  
#include <thrift/protocol/TBinaryProtocol.h> 


#include <fcgi_stdio.h>
#include <fcgiapp.h>


using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;


using namespace std;
using std::string;
using boost::shared_ptr;


inline void send_response(
            FCGX_Request& request, const std::string& resp_str) {


    FCGX_FPrintF(request.out, "Content-type: text/html;charset=utf-8\r\n\r\n");
    FCGX_FPrintF(request.out, "%s", resp_str.c_str());
    FCGX_Finish_r(&request);
}


int main(int argc, char **argv) {
    // step 1. init fcgi
    FCGX_Init();
    FCGX_Request request;
    FCGX_InitRequest(&request, 0, 0);


    // step 2. connect server rpc
    boost::shared_ptr<TSocket> socket(new TSocket("localhost", 9090));
    boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));


    transport->open();
    RecSysClient client(protocol);


    while(FCGX_Accept_r(&request) >= 0) {


        // http page -> client
        std::string send_data = FCGX_GetParam("QUERY_STRING", request.envp);


        string receive_data;


        // client -> server
        // server -> client
        client.rec_data(receive_data, send_data);


        cout << "receive http params: " << send_data << std::endl;
        cout << "receive server data: " << receive_data << endl;


        // client -> http page
        send_response(request, receive_data);
    }


    transport->close();


    return 0;
}

编译命令:

g++ -g -Wall -I./ -I/usr/local/include RecSys.cpp client.cpp -L/usr/local/lib/*.so -lthrift -lfcgi -o client  

[注意] 编译命令中,我将头文件的引入路径设置为: /usr/local/include
当不确定头文件存放路径时,可通过执行如下命令,进行全局搜索:
find / -name 库文件名

实例: find / -name fcgiapp.h  
说明:  
/ 表示全盘查找  
-name 名称  

2.6.2 汇总编译命令

Makefile修改为:

(py27tf) [root@singler gen-cpp]# cat Makefile
G++ = g++  
CFLAGS = -g -Wall  
INCLUDES = -I./ -I/usr/local/include/thrift  
LIBS = -L/usr/local/lib/*.so -lthrift -lfcgi  


OBJECTS = RecSys.cpp RecSys_constants.cpp RecSys_types.cpp RecSys_server.skeleton.cpp  
CLI_OBJECTS = RecSys.cpp client.cpp  


server: $(OBJECTS)  
        $(G++) $(CFLAGS) $(INCLUDES) $(OBJECTS) $(LIBS) -o server  


client: $(CLI_OBJECTS)  
        $(G++) $(CFLAGS) $(INCLUDES) $(CLI_OBJECTS) $(LIBS) -o client  


.PHONY: clean  
clean:  
        rm -rf server client 

2.6.3 运行

运行server

./server

观察nginx和cgi代理:

nginx:netstat -antup | grep nginx  
cgi代理:netstat -antup | grep 8088  

2.6.4 启动cgi服务

/usr/local/nginx/sbin/spawn-fcgi -a 127.0.0.1 -p 8088 -f /root/thrift_test/thrift_demo/gen-cpp/client  

2.6.5 启动nginx

设置反向代理,这里就不赘述了.

./sbin/nginx 

观察nginx是否成功

nginx:netstat -antup | grep nginx  

浏览器访问

http://192.168.153.151/recsys?item=111&userid=123

如果能正常返回信息,同时server端正常输出日志,说明没有问题了.

2.7 ab压测,模拟log日志

ab压测是一个很有用的工具

2.7.1 安装

yum -y install httpd-tools  

测试是否安装成功

ab -V 

2.7.2 执行ab命令,压力测试2.6的服务

(python2.7) [root@slave1 gen-cpp]# ab -c 20 -n 5000 'http://192.168.153.151/recsys?item=111&userid=123'

参数说明:

-c 一次产生的请求个数。默认是一次一个。  
-n 是所有的请求个数 

请求地址要带上引号,否则命令中,遇到&号后边的内容会被处理成一个后台任务.
这里写图片描述

2.8 写入log(glog - google 日志模块)

2.8.1 下载glog并安装

git clone https://github.com/google/glog.git  
./autogen.sh && ./configure && make && make install  

完成后,会在/usr/local/lib路径下看到libglog*一系列库

2.8.2 完善server代码

首先引入头文件:

#include <glog/logging.h>  

在主流程起始初始化glog

#定义log产生的位置  
FLAGS_log_dir = "/home/thrift_test_myself/thrift_cgi_demo/gen-cpp/logs";  
google::InitGoogleLogging(argv[0]); 

代码中log输出命令:

LOG(INFO) << data;  
LOG(ERROR) << data;  
LOG(WARNING) << data;  
LOG(FATAL) << data; 

在程序中输出FATAL级别的日志,会导致程序运行结束.若想观察日志连续输出,请使用INFO,或WARNING级别.

2.8.3 运行

运行server

g++ -g -Wall -I./ -I/usr/local/include RecSys.cpp RecSys_constants.cpp RecSys_types.cpp RecSys_server.skeleton.cpp -L/usr/local/lib/*.so -lthrift -lglog -o server  

通过spawn cgi启动client

/usr/local/bin/spawn-fcgi -a 127.0.0.1 -p 8088 -f /usr/local/src/thrift_test/gen-cpp/client  

启动nginx

./sbin/nginx

注意:
server -> client -> nginx 要依次起来,并进行检查确认.

2.8.4 压测

ab -c 2 -n 50 'http://192.168.153.151/recsys?itemid=111&userid=012&action=click&ip=10.11.11.10'

猜你喜欢

转载自blog.csdn.net/weixin_31351409/article/details/80341440