filebeat启动流程 讲解了filebeat
的启动流程,filebeat
在构建完crawler
对象,开始采集流程。
Crawler
的start
方法内,会启动Inputs
func (c *Crawler) Start(
pipeline beat.Pipeline,
r *registrar.Registrar,
configInputs *common.Config,
configModules *common.Config,
pipelineLoaderFactory fileset.PipelineLoaderFactory,
overwritePipelines bool,
) error {
...
for _, inputConfig := range c.inputConfigs {
err := c.startInput(pipeline, inputConfig, r.GetStates())
if err != nil {
return err
}
}
...
}
c.startInput(pipeline, inputConfig, r.GetStates())
方法初始化Input
。
- 首先构建
Input
对象 - 运行
Input
func (c *Crawler) startInput(
pipeline beat.Pipeline,
config *common.Config,
states []file.State,
) error {
if !config.Enabled() {
return nil
}
connector := channel.ConnectTo(pipeline, c.out)
p, err := input.New(config, connector, c.beatDone, states, nil)
...
// 开始收集
p.Start()
return nil
}
p.Start()
方法内启动Input
,他在一个单独的协程里运行。
这里的p
是对Input
的封装,他的Run
方法是对某个接口的实现,因为我们用来收集日志,所以我们只需要关心filebeat/input/log/input.go
文件内的Run
方法。Run
方法内部调用了Input
的scan
方法,开始采集数据。
// Run runs the input
func (p *Input) Run() {
...
p.scan()
...
}
scan
方法内首先获取所有的文件。其次获取文件状态,根据状态来判定收集最新数据,还是从历史文件收集。文件收集会构建Harvester
对象。
// Scan starts a scanGlob for each provided path/glob
func (p *Input) scan() {
var sortInfos []FileSortInfo
var files []string
// 获取应该获取到的所有文件
paths := p.getFiles()
var err error
...
for i := 0; i < len(paths); i++ {
var path string
var info os.FileInfo
if sortInfos == nil {
path = files[i]
info = paths[path]
} else {
path = sortInfos[i].path
info = sortInfos[i].info
}
select {
case <-p.done:
logp.Info("Scan aborted because input stopped.")
return
default:
}
newState, err := getFileState(path, info, p)
if err != nil {
logp.Err("Skipping file %s due to error %s", path, err)
}
// Load last state
lastState := p.states.FindPrevious(newState)
...
// Decides if previous state exists
if lastState.IsEmpty() {
logp.Debug("input", "Start harvester for new file: %s", newState.Source)
// 准备构建 harvester 了
err := p.startHarvester(newState, 0)
if err == errHarvesterLimit {
logp.Debug("input", harvesterErrMsg, newState.Source, err)
continue
}
if err != nil {
logp.Err(harvesterErrMsg, newState.Source, err)
}
} else {
// 从历史文件开始处理
p.harvestExistingFile(newState, lastState)
}
}
}
p.startHarvester(newState, 0)
内构建harvester
。(harvester
是另一个filebeat
官网描述的核心组件之一)
func (p *Input) startHarvester(state file.State, offset int64) error {
if p.numHarvesters.Inc() > p.config.HarvesterLimit && p.config.HarvesterLimit > 0 {
p.numHarvesters.Dec()
harvesterSkipped.Add(1)
return errHarvesterLimit
}
// Set state to "not" finished to indicate that a harvester is running
state.Finished = false
state.Offset = offset
// Create harvester with state
// 这部分构建了 harvester
h, err := p.createHarvester(state, func() { p.numHarvesters.Dec() })
if err != nil {
p.numHarvesters.Dec()
return err
}
// 配置 harvester
err = h.Setup()
if err != nil {
p.numHarvesters.Dec()
return fmt.Errorf("error setting up harvester: %s", err)
}
// Update state before staring harvester
// This makes sure the states is set to Finished: false
// This is synchronous state update as part of the scan
h.SendStateUpdate()
// 启动 harvester
if err = p.harvesters.Start(h); err != nil {
p.numHarvesters.Dec()
}
return err
}
p.createHarvester
构建harvester
p.Setup
配置harvester
。Setup
方法内会初始化文件相关的内容,以及构建文件reader
。p.harvesters.Start(h)
运行harvester
主要还是要看harvesters.Start
方法,会在单独的协程内运行harvester
。
func (r *Registry) Start(h Harvester) error {
// Make sure stop is not called during starting a harvester
r.Lock()
defer r.Unlock()
...
go func() {
defer func() {
r.remove(h)
r.wg.Done()
}()
// 异步运行
err := h.Run()
if err != nil {
logp.Err("Error running input: %v", err)
}
}()
return nil
}
harvester.Run
方法真是长。。
func (h *Harvester) Run() error {
// 这坨简直了
for {
...
// 读取文件内容
message, err := h.reader.Next()
// 糟糕的异常处理。。。
if err != nil {
switch err {
...
}
return nil
}
state := h.getState()
startingOffset := state.Offset
state.Offset += int64(message.Bytes)
...
// 读取到的文件内容
text := string(message.Content)
...
// 数据内容都包装在 data 内,harvester 发送 data,其实就是 forwarder 转发的
if !h.sendEvent(data, forwarder) {
return nil
}
// Update state of harvester as successfully sent
h.state = state
}
}
h.sendEvent(data, forwarder)
这段代码将采集的数据发送到下游,内部其实就是用forwarder
转发了数据。
到这里数据的采集流程应该就差不多了,剩下的是数据的发送流程。