基于kafka和zookeeper的日志收集平台
平台架构图

一 、环境准备
kafka2.12,centos7,nginx,filebeat,zookeeper,python3.6,mysql
1.准备好3台linux虚拟机搭建nginx和kafka集群
2.修改主机名分别为:ngixn-kafka01,ngixn-kafka02,ngixn-kafka03,
3.修改机器为静态ip地址:
进入 /etc/sysconfig/network-scripts/ 编辑 ens33文件

配置dns:
1 [root@nginx-kafka01 ~]# cat /etc/resolv.conf 2 # Generated by NetworkManager 3 nameserver 114.114.114.114
4.每台机器上写好域名解析: 1 [root@nginx-kafka01 ~]# cat /etc/hosts
2 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 3 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 4 192.168.133.157 nginx-kafka01 5 192.168.133.156 nginx-kafka02 6 192.168.133.154 nginx-kafka03
# DNS解析过程:
1. 浏览器的缓存
2. 本地的hosts文件 --linux /etc/hosts
3. 请求本地域名服务器 --linux /etc/resolv.conf
5.在机器上安装必备的软件:
yum install wget lsof vim -y
6、安装时间同步服务
1 yum -y install chrony 2 systemctl enable chronyd 3 systemctl start chronyd 4 5 设置时区: 6 cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
7、关闭防火墙
[root@nginx-kafka01 ~]# systemctl stop firewalld
[root@nginx-kafka01 ~]# systemctl disable firewalld
关闭selinux:
vim /etc/selinux/config
SELINUX=disabled
selinux关闭 需要重启机器
8.每台机器安装时间同步服务
yum -y install chrony #安装chrony
systemctl enable chronyd #设置开机自启
systemctl start chronyd #开启chory
设置时区:
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
二、nginx平台的搭建
1.安装好epel源和nginx:
1 yum install epel-release -y 2 yum install nginx -y
2.启动nginx
systemctl start nginx #启动nginx
systemctl enable nginx # 设置开机自启
3.编辑nginx配置文件
[root@nginx-kafka01 ~]# cd /etc/nginx/
[root@nginx-kafka01 nginx]# ls
conf.d fastcgi.conf.default koi-utf mime.types.default scgi_params uwsgi_params.default
default.d fastcgi_params koi-win nginx.conf scgi_params.default win-utf
fastcgi.conf fastcgi_params.default mime.types nginx.conf.default uwsgi_params
主配置文件: nginx.conf
... #全局块
events { #events块
...
}
http #http块
{
... #http全局块
server #server块
{
... #server全局块
location [PATTERN] #location块
{
...
}
location [PATTERN]
{
...
}
}
server
{
...
}
... #http全局块
}
1、全局块:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
2、events块:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。
3、http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。
4、server块:配置虚拟主机的相关参数,一个http中可以有多个server。
5、location块:配置请求的路由,以及各种页面的处理情况
#配置文件修改
vim nginx.conf
将
listen 80 default_server;
修改成:
listen 80;
vim /etc/nginx/conf.d/sc.conf
server {
listen 80 default_server;
server_name www.hao123.com;
root /usr/share/nginx/html;
access_log /var/log/nginx/sc/access.log main;
location / {
}
}
4.nginx语法检测
[root@nginx-kafka01 nginx]# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
5.重新加载nginx
nginx -s reload
三、项目总体架构及技术介绍
1.负载均衡:nginx反向代理
什么是反向代理?
反向代理就是代理服务器,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,再返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器 IP地址。
DNS也可以做负载均衡,一条域名可以解析成多个ip地址,一般来说会轮询解析成各个ip,但是如果其中一台服务器挂了,dns不会立马把这个ip地址去掉,可能会造成访问失败,虽然客户端有重试,但是还是会影响客户体验,所以我们在应用web前面添加反向代理,能做到负载均衡,安全性高。
什么是正向代理?
正向代理就是代理客户机,
总的来说,如果我们客户端自己用,就是正向代理。如果是在服务器用,用户无感知,就是反向代理。
2.高可用:keepalived
反向代理机使用keepalived双vip互为主备做高可用,提高资源利用率
keepalived实现的原理是使用vrrp协议
vip就可以实现高可用了,双vip是为了解决backup资源闲置
3.kafka
3.1 我们的三台Nginx可以用脚本将数据一起传到mysql中,那为什么还要弄kafka-zookeeper集群来统一处理数据再传入数据库?
- 故障发生时,方便定位问题
- 日志集中管理,后续需要日志的程序直接从kafka获取日志即可,尽可能的减少日志处理对nginx的影响
3.2 市面上常见的消息中间件有哪些?

3.3 消息中间件的作用?
#####日志收集
日志集中管理,后续需要日志的程序直接从kafka获取日志即可,尽可能的减少日志处理对nginx的影响
#####业务解耦
当一个程序含有多种业务时,业务之间会互相影响。现在让各个业务独立发展,每个地方都只做一件事情。只要一个地方负责了多项事情,就存在解耦的可能。
业务解耦的实现?
当一个程序含有多种业务,各个业务互不影响且不需向前端传递任何值。可以将各个业务独立出来,每个业务独立完成。技术方案采用:消息队列
优点:提高扩展性,因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。
#####流量削峰
消息队列解决流量削峰,要对流量进行削峰,最容易想到的解决方案就是用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送出去。
3.4 kafka的通信方式是什么?
发布订阅模式,,消费者可以订阅一个或多个topic,消费者可以消费该topic中所有的数据,同一条数据可以被多个消费者消费,数据被消费后不会立马删除。在发布-订阅消息系统中,消息的生产者称为发布者,消费者称为订阅者。并发性要好,不过并发性也会取决于partition的数量,broker的数量
3.5 kafka名词解释
broker:kafka服务器节点
topic:主题–>消息的分类,比如nginx还是mysql日志给不同的主题就是不同的类型
partition:分区,提高吞吐量,提高并发性能,提高效率
一个topic可以对应几个partition,一般partition的数量对应broker的数量
但是多个partition会造成消息顺序混乱,如果对消息顺序有要求就只设置一个partition就可以了
replica:副本,kafka里的高可用–>就是完整的分区备份
自己本身就是一个副本,第二个副本放在其他的broker上,实现高可用
生产者(producer):写入数据的–>filebeat
消费者(consumer):拿取数据的
leader:(唯一)每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。
follower:(n-1个)Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这个follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower。
ISR(in-sync-replica):集合列表 需要同步的follower集合
比如5个副本,1个leader 4个follower --> 同步列表
有一条消息来了,leader怎么知道要同步到哪些副本呢?根据ISR来,如果一个follower挂了或者卡住或者同步过慢,那就从这个列表里踢出了
如果leader挂了,就会从ISR里面再选出一个leader,一般来说是ISR中的第一个
4.问题总结
4.1 kafka如何保证高可用?
多个broker+多个partition+多个replica
broker数量如果和replica数量一致,可以坏掉n-1台
如果一个follower挂了或者卡住或者同步过慢,那就从这个列表里踢出了
如果leader挂了,就会从ISR里面再选出一个leader,一般来说是ISR中的第一个
如果有一个机器宕机,后续启动之后想要重新加入ISR,必须得同步到ISR中的HW值(最高水位线)才可以加入进来
4.2 一个partition有n个副本,那数据写入哪个副本呢?
生产者和消费者只和leader打交道,所以数据写入leader,leader再根据ISR同步给其他follower
4.3 生产者数据一致性问题
如果生产者给leader发送7条数据,但是follower才同步3条,leader就挂了,导致数据的不一致性?怎么解决数据的一致性问题?
producer生产者可以通过request.required.acks 参数设置
acks=0:生产者不会等待任何来自服务器的响应。
如果当中出现问题,导致服务器没有收到消息,那么生产者无从得知,会造成消息丢失
由于生产者不需要等待服务器的响应所以可以以网络能够支持的最大速度发送消息,从而达到很高的吞吐量
acks=1(默认值):只要集群的Leader节点收到消息,生产者就会收到一个来自服务器的成功响应
如果消息无法到达Leader节点(例如Leader节点崩溃,新的Leader节点还没有被选举出来)生产者就会收到一个错误响应,为了避免数据丢失,生产者会重发消息
如果一个没有收到消息的节点成为新Leader,消息还是会丢失
此时的吞吐量主要取决于使用的是同步发送还是异步发送,吞吐量还受到发送中消息数量的限制,例如生产者在收到服务器响应之前可以发送多少个消息
acks=-1:只有当所有参与复制的节点全部都收到消息时,生产者才会收到一个来自服务器的成功响应
这种模式是最安全的,可以保证不止一个服务器收到消息,就算有服务器发生崩溃,整个集群依然可以运行
延时比acks=1更高,因为要等待不止一个服务器节点接收消息
根据实际的应用场景,我们设置不同的 acks,以此保证数据的可靠性。
4.4 消费者数据一致性问题
消费者消费数据时,引入了High Water Mark 机制,木桶效应,只能消费到ISR列表里偏移量最少的副本的消息数量
同一个消费组里面的消费者在同一时刻只能消费一个partition,且消费者1在消费这个partition时,消费者2就不能消费这个partition了,不然会导致数据混乱
但是不同的消费组是独立的,不同消费组里面的消费者是可以消费同一个partition了,不会导致自己的消费组消费的数据混
4.5 为什么生产者可以随便连接哪一台机器,不是只能和leader打交道吗?(消费者消费leader的数据同理)
生产者跟任何一台broker连接都可以,broker之间会有协商,broker会返回当前请求副本leader的信息,最后生产者再跟leader交互。
4.6 消费者如何知道自己已经消费到了哪些数据呢?下次继续消费?
消费者消费的时候,会记录自己的消费偏移量,消费偏移量可以自己保存在本地,也可以提交到kafka的_consumer_offsets主题里保存。会保存topic id、partition和偏移量
四、kafka以及zookeeper集群搭建
zookeeper:分布式应用协调管理服务:统一配置管理、域名管理、分布式数据存储、集群管理
1. 安装
安装java:yum install java wget -y 安装kafka: wget https://mirrors.bfsu.edu.cn/apache/kafka/2.8.1/kafka_2.12-2.8.1.tgz --no-check-certificate 解包: tar xf kafka_2.12-2.8.1.tgz 使用自带的zookeeper集群配置 安装zookeeper: wget https://mirrors.bfsu.edu.cn/apache/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz 解压到 /opt目录下
2.配置 kafka
修改config /server.properties: broker.id=0 ####每台机器不唯一#### listeners=PLAINTEXT://nginx-kafka01:9092 zookeeper.connect=192.168.133.157:2181,192.168.133.156:2181,192.168.133.154:2181
3.配置zookeeper
进入/opt/apache-zookeeper-3.6.3-bin/confs cp zoo_sample.cfg zoo.cfg 修改zoo.cfg, 添加如下三行: server.1=192.168.133.157:3888:4888 #3888和4888都是端口,一个用于数据传输,一个用于检验存活性和选举 server.2=192.168.133.156:3888:4888 server.3=192.168.133.154:3888:4888
创建/tmp/zookeeper目录 ,在目录中添加myid文件,文件内容就是本机指定的zookeeper id内容
如:在192.168.133.157机器上
echo 1 > /tmp/zookeeper/myid
4.启动
启动zookeeper:
bin/zkServer.sh start #进入zookeeper文件执行
启动Kafka:
bin/kafka-server-start.sh -daemon config/server.properties #进入kafka文件执行
注意,开启zookeeper和kafka的时候,一定是先启动zookeeper,再启动kafka
关闭服务的时候,kafka先关闭,再关闭zookeeper
5.测试
###创建topic
创建生产者

创建消费者

五、filebeat部署
1.安装filebeat
rpm --import https://packages.elastic.co/GPG-KEY-elasticsearch
编辑/etc/yum.repos.d/fb.repo文件

yum安装filebeat
yum install filebeat -y
rpm -qa |grep filebeat #可以查看filebeat有没有安装 rpm -qa 是查看机器上安装的所有软件包
rpm -ql filebeat 查看filebeat安装到哪里去了,牵扯的文件有哪些
设置开机自启
systemctl enable filebeat
2. 配置filebeat
修改配置文件/etc/filebeat/filebeat.yml ####先备份文件再清空 >filebeat.yml
filebeat.inputs: - type: log # Change to true to enable this input configuration. enabled: true # Paths that should be crawled and fetched. Glob based paths. paths: - /var/log/nginx/sc/access.log #==========------------------------------kafka----------------------------------- output.kafka: hosts: ["192.168.229.139:9092","192.168.229.140:9092"] topic: nginxlog keep_alive: 10s
#注意此处文件格式要求非常严格,千万不能出现错误
3.创建主题nginxlog
bin/kafka-topics.sh --create --zookeeper 192.168.133.157:2181 --replication-factor 3 --partitions 1 --topic nginxlog
4. 启动服务
systemctl start filebeat
5.修改ip映射
如果想要通过域名访问,需要修改客户端本地的hosts文件,添加nginx机器的ip和域名对应的映射关系
window10 在 C:\Windows\System32\drivers\etc 下 编辑hosts文件,添加如下:
192.168.133.157 www.hao123.com 192.168.133.156 www.hao123.com 192.168.133.154 www.hao123.com
六、zookeeper在kafka集群中的作用
1.zookeeper中leader的选举:
一致性算法 zab
少数服从多数原则,票数过半的当选为leader 票数>= n//2 + 1
2.leader和follower的作用
客户端连接任意一台zk都可以操作,但是数据新增修改等事务操作必须在leader上运行,客户端如果连接到follower上进行事务操作,follower会返回leader的ip,最终客户端还是会在leader上操作。
如果进行查询操作,可以直接连接follower进行查询操作
follower的作用:查询,选举
3. follower同步leader数据的方式
leader和follower数据的同步,只要过半节点同步完成,就表示数据已经commit
zookeeper不是强一致性,它属于最终一致性
查询体验不会很差,因为每个节点数据量默认不超过1M,同步很快
zookeeper集群节点存活数必须过半,集群才能正常使用,所以一般zk集群的节点数都设置为奇数,方便选举
4. zookeeper在kafka中的作用
1.保存kafka的元数据,topic,partition,副本信息
2.选举controller,通过抢占的方式选举,先到先得controller,选举出的kafka controller管理kafka副本的leader和follower同步、选举
kafka3.0版本已经脱离zookeeper,kafka自己实现zookeeper功能
七、数据注入数据库
1.数据需求分析
需要nginx日志的ip,时间,带宽字段
将ip字段解析成相应的省份、运营商
存入数据库的字段: id, 时间, 省份, 运营商, 带宽
2.具体步骤

###创建数据库表
create table nginxlog ( id int primary key auto_increment, dt datetime not null, prov int , isp int, bd float ) CHARSET=utf8;
#####编写python脚本
import json
import requests
import time
import pymysql
taobao_url = "https://ip.taobao.com/outGetIpInfo?accessKey=alibaba-inc&ip="
# 查询ip地址的信息(省份和运营商isp),通过taobao网的接口
def resolv_ip(ip):
response = requests.get(taobao_url + ip)
if response.status_code == 200:
tmp_dict = json.loads(response.text)
prov = tmp_dict["data"]["region"]
isp = tmp_dict["data"]["isp"]
return prov, isp
return None, None
# 将日志里读取的格式转换为我们指定的格式
def trans_time(dt):
# 把字符串转成时间格式
timeArray = time.strptime(dt, "%d/%b/%Y:%H:%M:%S")
# timeStamp = int(time.mktime(timeArray))
# 把时间格式转成字符串
new_time = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
return new_time
# 从kafka里获取数据,清洗为我们需要的ip,时间,带宽
from pykafka import KafkaClient
client = KafkaClient(hosts="192.168.133.157:9092,192.168.133.156:9092,192.168.133.154:9092")
topic = client.topics['nginxlog']
balanced_consumer = topic.get_balanced_consumer(
consumer_group='testgroup',
auto_commit_enable=True,
zookeeper_connect='nginx-kafka01:2181,nginx-kafka02:2181,nginx-kafka03:2181'
)
# consumer = topic.get_simple_consumer()
for message in balanced_consumer:
if message is not None:
line = json.loads(message.value.decode("utf-8"))
log = line["message"]
tmp_lst = log.split()
ip = tmp_lst[0]
dt = tmp_lst[3].replace("[", "")
bt = tmp_lst[9]
dt = trans_time(dt)
prov, isp = resolv_ip(ip)
if prov and isp:
print(prov, isp, dt,bt)
db = pymysql.connect(host="192.168.133.157", user="rhd", password="123456", port=3306, db="consumers", charset="utf8")
cursor = db.cursor()
try:
cursor.execute('insert into nginxlog(dt,prov,isp,bd) values("%s", "%s", "%s", "%s")' % (dt, prov, isp, bt))
db.commit()
print("保存成功")
except Exception as err:
print("修改失败", err)
db.rollback()
db.close()

浙公网安备 33010602011771号