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'