海量日志收集项目总结(2)logtranfer
简介
logtranfer的作用是:把日志从kafka消息队列中读出来,发往ES,并连接kibana,做可视化展示。
模块架构为:
项目源码
项目的目录结构
各部分源码
main.go:
package main
import (
"github.com/wind-zhou/log_transfer/conf"
"github.com/wind-zhou/log_transfer/es"
"gopkg.in/ini.v1"
"fmt"
"github.com/wind-zhou/log_transfer/kafka"
)
//1.将日志数据取出
//2.发往ES
var (
cfg = new(conf.LogTransfer)
)
func main(){
//0. 加载配置文件
err := ini.MapTo(cfg, "./conf/cfg.ini")
if err != nil {
fmt.Println("load ini failed")
return
}
fmt.Println(cfg)
//初始化es
err=es.Init(cfg.EsCfg.Address)
if err != nil {
fmt.Printf("init es failed err=%v
",err)
}
fmt.Println("init es success")
//1.初始化kafka
//1.1 链接kafka
//1.2 每个分区读数据发往es
err=kafka.Init([]string{cfg.KafkaCfg.Address},cfg.KafkaCfg.Topic)
if err != nil {
fmt.Printf("init kafka failed err=%v
",err)
return
}
select {
}
}
kafka.go:
package kafka
import (
"fmt"
"github.com/Shopify/sarama"
"github.com/wind-zhou/log_transfer/es"
)
//初始化client 消费者
//从kafka取数据发给es
func Init(address []string,topic string) error {
consumer, err := sarama.NewConsumer(address, nil)
if err != nil {
fmt.Printf("fail to start consumer, err:%v
", err)
return err
}
partitionList, err := consumer.Partitions(topic) // 根据topic取到所有的分区
if err != nil {
fmt.Printf("fail to get list of partition:err%v
", err)
return err
}
fmt.Println("分区列表:",partitionList)
for partition := range partitionList { // 遍历所有的分区
// 针对每个分区创建一个对应的分区消费者
pc, err := consumer.ConsumePartition("web_log", int32(partition), sarama.OffsetNewest)
if err != nil {
fmt.Printf("failed to start consumer for partition %d,err:%v
", partition, err)
return err
}
//defer pc.AsyncClose()
// 异步从每个分区消费信息
go func(sarama.PartitionConsumer) {
for msg := range pc.Messages() {
fmt.Printf("Partition:%d Offset:%d Key:%v Value:%v
", msg.Partition, msg.Offset, msg.Key, string(msg.Value))
//发送给es
ld:=es.LogData{
Topic: topic,
Data: string(msg.Value),
}
es.SendToEsChan(&ld)//把读取的分区数据发送到一个通道
if err != nil {
fmt.Printf("es.SendToEs err=%v
",err)
return
}
}
}(pc)
}
return err
}
es.go:
package es
import (
"context"
"fmt"
"github.com/olivere/elastic"
"strings"
"time"
)
type LogData struct {
Topic string `json:"topic"`
Data string`json:"data"`
}
var (
client *elastic.Client
ch =make( chan *LogData,100000)
)
//初始化es
//准备接受Kafka的数据
func Init(address string)(err error){
if !strings.HasPrefix(address,"http://"){
address="http://"+address
}
client, err = elastic.NewClient(elastic.SetURL(address))
if err != nil {
// Handle error
return
}
fmt.Println("connect to es success")
go SendToEs()
return
}
//SendToEs 发送数据到es
func SendToEsChan(msg *LogData) {
ch <-msg
}
func SendToEs(){
for{
select {
case msg:= <-ch:
put1, err := client.Index().Index(msg.Topic).Type("xxx").BodyJson(msg).Do(context.Background())
if err != nil {
// Handle error
fmt.Println(err)
}
fmt.Printf("Indexed user %s to index %s, type %s
", put1.Id, put1.Index, put1.Type)
default:
time.Sleep(time.Second)
}
}
}
cfg.go:
package conf
type LogTransfer struct{
KafkaCfg`ini:"kafka"`
EsCfg`ini:"es"`
}
type KafkaCfg struct {
Address string`ini:"address"`
Topic string`ini:"topic"`
}
type EsCfg struct {
Address string`ini:"address"`
}
cfg.ini:
[kafka]
address=192.168.43.99:9092
topic=web_log
[es]
address=127.0.0.1:9200
项目分析
这部分的主角其实只有两个:kafka和ES,kibana只是个工具人(因为不需要用go语言操作kibana)。
- kafka部分需要写一个kafka的消费者,负责从消息队列中按照特定的topic取出日志信息。
- ES部分需要编写代码,将取出的数据写入ES。
各部分操作都需要初始化,kafka需要知道主机的地址,以及所取日志的topic;ES需要知道主机的地址,因此这些参数可以写入配置文件中,也就是cfg.ini, cfg.go里面声明一些结构体,用于之后配置文件解析。
注:
上面给的源码中,kafka消费者所取的日志topic已经写死到了配置文件中,也就是kafka消费者,只拉取web_log主题的日志,如果想要拉取里面所有的topic日志,可以仿照前面的logagent,加一个etcd模块,用于存储之前存入的topic。
main.go中解析配置文件代码:
kafka消费者:
Kafka消费者的逻辑为:
- 传入kafka主机地址,连接上kafka,创建一个消费者的
- 传入日志的topic,获取到搞topic所有的分区
- 遍历所有的分区,并在每个分区创建一个goroutine,分别去读取日志
- 每个goroutine内,将读取的信息发往ES
ES操作:
ES部分的逻辑为:
- 传入ES主机的地址,连接ES,并产生一个ES的client
- 开启一个goroutine,将数据传入ES的指定的Index,Type。
OK,这部分核心代码就结束了。
一些细节:
- 消费者是按topic从kafka读取数据,因此在传入ES时,可以按topic创建Index
- 消费者读取的数据为字符串或字符切片。先构造成一个结构体,发送到ES时,会将其转换成 json格式。
- main程序最后加了个select{},用于等待,否则主程序会退出
这里还做了一些优化,如kafka拿到数据后,先将其送入一个通道,ES端开启的goroutine是在通道中拿数据并发送。
项目调试
测试环境搭建
-
开启kafka
- 开启zookeeper :
.inwindowszookeeper-server-start.bat .configzookeeper.properties
- 开启kafka :
.inwindowskafka-server-start.bat .configserver.properties
- 开启zookeeper :
-
开启etcd
-
开启es :
binelasticsearch.bat
-
开启kaibana,连接到ES
开启kibana步骤:
- 修改kibana配置文件kibana.yaml中的内容
设置ES主机地址:
设置中文编码:
- 开启kibana :
binkibana.bat
- 访问kibana :
http://127.0.0.1:5601
-
开启前面写的logagent
-
开启logtranfer
调试
- 打开web_log主题日志所在的文件nginx.log , 在里面写入数据
- logagent端返回的结果
- logtranfer端 返回结果
可见收到的web_log日志被存储到了ES的Index=web_log,Type=xxx中。
- 在kibana中索引
后记
到此,这个项目完结,第一次写实战博客,写的很烂,但作为一个开始吧!
本次项目简单学了一些新东西:
ES,kafka,zookeeper,kinbana等。
另外,这个项目是看网上盗版视频缩写,非常的粗糙,logagent和logtranfer两部分并没有很好的衔接起来,两个模块没有整合到在一起,例如logtranfer只读取了logagent中web-log主题日志,没有读取其所有topic日志。
这可以进行优化,大致的思路是,在logagent端每次读取etcd新变化后,记录下该配置,并传递给logtranfer,然后logtranfer端根据此配置去开启或关闭goroutine。