OpenResty 是一个基于 Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty通过汇聚各种设计精良的 Nginx模块(主要由 OpenResty 团队自主开发),从而将 Nginx有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
OpenResty的目标是让你的Web服务直接跑在 Nginx服务内部,充分利用 Nginx的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
OpenResty = Nginx + Lua,是一个增强的Nginx,可以编写lua脚本实现非常灵活的逻辑了
yum install -y pcre-devel openssl-devel gcc curl
yum install yum-utils
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
yum install openresty
/usr/local/openresty
/usr/local/openresty/nginx/sbin/nginx
或
service openresty start
image-20181225205140044
2.2 安装openresty依赖RPM包
yum install -y readline-devel pcre-devel openssl-devel gcc perl wget
2.3 下载安装openresty安装包
wget https://openresty.org/download/openresty-1.13.6.2.tar.gz
2.4 解压openresty安装包
tar -zxvf openresty-1.13.6.2.tar.gz -C /usr/local/src/
2.5 编译openresty
进入到openresty自带的和LuaJIT整合的目录,然后编译安装
#为了nignx可以和lua脚本结合,所以才编译LuaJIT
cd /usr/local/src/openresty-1.13.6.2/bundle/LuaJIT-2.1-20180420
make && make install
切换到openresty的安装目录,预编译openresty
cd /usr/local/src/openresty-1.13.6.2
./configure
编译安装
gmake && gmake install
2.6 启动Nginx
启动openresty配套的nginx
/usr/local/openresty/nginx/sbin/nginx
查看nginx的详细信息
/usr/local/openresty/nginx/sbin/nginx -V
2.7使用浏览器访问nginx
image-20181225205140044
3.1 修改openresty下nginx的配置文件
vi /usr/local/openresty/nginx/conf/nginx.conf
image-20181225210248780
在http的配置下添加如下配置
location /lua {
default_type ‘text/html’;
content_by_lua ‘ngx.say(“hello 51doit”)’;
}
3.2 在浏览器中进行测试
image-20181225210805920
3.3 配置伪装图片的静态地址
location /log.gif {
#伪装成gif文件
default_type image/gif;
#本身关闭access_log
access_log off;
#返回空图片
empty_gif;
}
3.4 使用lua的log_by_lua_file将参数写入到指定的日志文件中
location /log.gif {
#伪装成gif文件
default_type image/gif;
#本身关闭access_log
access_log off;
#使用lua将nginx的接收的参数写入到日志文件中
log_by_lua_file 'conf/log.lua';
#返回空图片
empty_gif;
}
在nginx的conf目录下创建一个log.lua文件
vi /usr/local/openresty/nginx/conf/log.lua
编写lua脚本
-- 引入lua所有解析json的库
local cjson = require "cjson"
-- 获取请求参数列表
local request_args_tab = ngx.req.get_uri_args()
-- 使用lua的io打开一个文件,如果文件不存在,就创建,a为append模式
local file = io.open("/logs/access.log", "a")
-- 定义一个json对象
local log_json = {}
-- 将参数的K和V迭代出来,添加到json对象中
for k, v in pairs(request_args_tab) do
log_json[k] = v
end
-- 将json写入到指定的log文件,末尾追加换行
file:write(cjson.encode(log_json), "\n")
-- 将数据写入
file:flush()
创建存放日志的目录并设置写入权限
mkdir /logs
chmod o+w /logs
#或者将/logs目录的所属用户改成nobody
chown -R nobody:nobody /logs
如果一直往一个文件中写入数据,这个日志文件会过大,造成读写效率变低,现在按照小时生成文件
-- 引入lua用来解析json的库
local cjson = require "cjson"
-- 获取请求参数列表
local request_args_tab = ngx.req.get_uri_args()
-- 获取当前系统时间
local time = os.date("%Y%m%d%H",unixtime)
-- 使用lua的io打开一个文件,如果文件不存在,就创建,a为append模式
local path = "/logs/access-" .. time .. ".log"
local file = io.open(path, "a")
-- 定义一个json对象
local log_json = {}
-- 将参数的K和V迭代出来,添加到json对象中
for k, v in pairs(request_args_tab) do
log_json[k] = v
end
-- 将json写入到指定的log文件,末尾追加换行
file:write(cjson.encode(log_json), "\n")
-- 将数据写入
file:flush()
在nginx所在的机器上安装Flume,使用TailDirSource和KafkaChannel将数据采集到Kafka中
Flume TailDirSource的优化
注意1:TailDirSource有一个小bug,即log文件重命名后会重复采集数据,需要修改flume的源代码ReliableTaildirEventReader,使用Linux的INODE作为文件的唯一标识,这样就不会重复采集数据了
248 //if (tf == null || !tf.getPath().equals(f.getAbsolutePath())) {
249 if (tf == null) {
136 //if (tf != null && tf.updatePos(path, inode, pos)) {
137 if (tf != null && tf.updatePos(tf.getPath(), inode, pos)) {
注意2:TailDirSource不能递归找一个目录及其子目录产生的log文件,修改了源代码,具体代码可以多全世界最大的男性交友社区查找: https://github.com/qwurey/flume-source-taildir-recursive
KafkaChannel其实是Kafka的一个Producer,我们可以修改KafkaChannel的配置(即Producer的配置)实时优化和保证数据不丢
优化1:配置KafkaChannel的ACK机制-1,保证数据不丢,而且创建Kafka的topic时,副本数量为2,即数据就保存2份,数据保存2份已经很靠谱了,我们公司的Kafka集群配置是2个cup,24核,48G内存,并且是SSD的磁盘。并且创建topic分区数量的时候,分区的数量为 =(机器的数量 * cpu的数量 * 每个cpu的核数)
优化2:设置KafkaChannel的压缩和失败重试次数
image-20190423165531008
kafka Producer参数配置地址 http://kafka.apache.org/0110/documentation.html#producerconfigs
value.serializer=cn._51doit.kafka.ProtoBuffSerializer / Serializer
acks=-1
buffer.memory = 33554432 * 1.5
compression.type = snappy
retries = 3
batch.size = 16384 * 2
max.request.size = (可选)
request.timeout.ms = (可选)
上面是我从Kafka生产者Producer中挑选的一些参数,如果配置到Flume的KafkaChannel应该如下配置
#配置ACK应答机制
a1.channels.c1.kafka.producer.acks = -1
#失败重试次数
a1.channels.c1.kafka.producer.retries = 10
#是snappy进行压缩
a1.channels.c1.kafka.producer.compression.type = snappy
优化3:数据写入到Kafka之前,自定义序列化器,将数据先序列化,这样可以减少网络IO的压力
ProtoBuff和Kyro、ProtoStuff,现在我们使用ProtoBuff和ProtoStuff(其中的一种)对数据进行序列化
Protobuf(Google公司开发的)是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储
https://github.com/protocolbuffers/protobuf
数据序列化、反序列化速度快,压缩比高
ProtoBuff,先定义一个.proto的schema的配置文件,然后通过这个配置文件和Maven插件,生成JavaBean
然后是有查看生成一个JavaBean
实现Kafka的序列化接口实现习序列化(Kafka的生产者调用的),把Bean变成byte数组
LogBeanSerializer implements Serializer
将序列化器对应的程序打包,放入到Flume的lib下
然后在flume的配置文件中添加序列化类的全类名
a1.channels.c1.kafka.producer.value.serializer = cn._51doit.kafka.proto.LogBeanSerializer
实现Kafka的反序列化接口实现习序列化(Kafka的消费者),把byte数组变成Bean
LogBeanDeserializer implements Deserializer
val kafkaParams = Map[String, Object](
“bootstrap.servers” -> “node-2.51doit.cn:9092,node-3.51doit.cn:9092”,
“key.deserializer” -> classOf[StringDeserializer],
“value.deserializer” -> classOf[LogBeanDeserializer],
“group.id” -> “g919”,
“auto.offset.reset” -> “earliest”,
“enable.auto.commit” -> (false: java.lang.Boolean)
)
protostuff(java编写)是一个基于protobuf实现的序列化方法,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化。使用它也非常简单
https://github.com/protostuff/protostuff
需求,将flume采集的数据(json)转换成Java对象,然后是有protostuff序列化成二进制保存到Kafka,虽然效率了计算机的资源(CPU和内存),但是大大的减少了网络IO和存储空间,用时间换空间
以后Flink或SparkSteaming消费数据的时候,要定义反序列化器
1.引入protostuff的maven依赖
<!-- 引入ProtoStuff的依赖 -->
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.5.9</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.5.9</version>
</dependency>
2.引入Kafka的依赖
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.11.0.3</version>
<scope>provided</scope>
</dependency>
4.安装openresty和kafka整合插件
4.1 插件地址
https://github.com/doujiang24/lua-resty-kafka
4.2 下载插件源码
wget https://github.com/doujiang24/lua-resty-kafka/archive/master.zip
4.3 解压lua-resty-kafka的源码
首先安装unzip解压工具,如果没有按照unzip会报错
image-20181225212016856
使用yum安装unzip
yum install -y unzip
解压lua-resty-kafka的源码
unzip master.zip -d /usr/local/src/
在/usr/local/openresty/lualib下创建一个kafka目录,用于保存lua-resty-kafka整合的模块代码
mkdir /usr/local/openresty/lualib/kafka
将lua-resty-kafka整合的模块代码拷贝到/usr/local/openresty/lualib/kafka目录
cp -r /usr/local/src/lua-resty-kafka-master/lib/resty/ /usr/local/openresty/lualib/kafka/
4.4 修改nginx配置文件
vi /usr/local/openresty/nginx/conf/nginx.conf
image-20181225215256197
在http配置的下面、server配置同级的部分添加如下配置
lua_package_path “/usr/local/openresty/lualib/kafka/?.lua;;”;
image-20181225215953454
在server配置的下面添加如下配置
location /log {
}
4.5启动zookeeper
依次启动zookeeper集群中的所有节点
/bigdata/zookeeper-3.4.10/bin/zkServer.sh start
4.6启动kafka
依次启动kafka集群中的所有节点
/bigdata/kafka_2.11-0.10.2.1/bin/kafka-server-start.sh -daemon /bigdata/kafka_2.11-0.10.2.1/config/server.properties
创建一个名为test的topic
/bigdata/kafka_2.11-0.10.2.1/bin/kafka-topics.sh --zookeeper localhost:2181 --create --topic test --replication-factor 3 --partitions 3
–zookeeper localhost:2181 --topic test --from-beginning