极速精简 Go 版 Logstash

前言

今天来介绍 go-zero 生态的另一个组件 go-stash。这是一个 logstash 的 Go 语言替代版,我们用 go-stash 相比原先的 logstash 节省了2/3的服务器资源。如果你在用 logstash,不妨试试,也可以看看基于 go-zero 实现这样的工具是多么的容易,这个工具作者仅用了两天时间。

整体架构

先从它的配置中,我们来看看设计架构。

Clusters:
  - Input:
      Kafka:
        # Kafka 配置 --> 联动 go-queue
    Filters:
        # filter action
      - Action: drop            
      - Action: remove_field
      - Action: transfer      
    Output:
      ElasticSearch:
        # es 配置 {host, index}

看配置名:kafka 是数据输出端,es 是数据输入端,filter 抽象了数据处理过程。

对,整个 go-stash 就是如 config 配置中显示的,所见即所得。

image.png

启动

stash.go 的启动流程大致分为几个部分。因为可以配置多个 cluster,那从一个 cluster 分析:

  1. 建立与 es 的连接【传入 es 配置】
  2. 构建 filter processorses 前置处理器,做数据过滤以及处理,可以设置多个】
  3. 完善对 es 中 索引配置,启动 handle ,同时将 filter 加入handle【处理输入输出】
  4. 连接下游的 kafka,将上面创建的 handle 传入,完成 kafkaes 之间的数据消费和数据写入

MessageHandler

在上面架构图中,中间的 filter 只是从 config 中看到,其实更详细是 MessageHandler 的一部分,做数据过滤和转换,下面来说说这块。

以下代码:https://github.com/tal-tech/go-stash/tree/master/stash/handler/handler.go

type MessageHandler struct {
    writer  *es.Writer
    indexer *es.Index
    filters []filter.FilterFunc
}

这个就对应上面说的,filter 只是其中一部分,在结构上 MessageHandler 是对接下游 es ,但是没有看到对 kafka 的操作。

别急,从接口设计上 MessageHandler 实现了 go-queueConsumeHandler 接口。

这里,上下游就串联了:

  1. MessageHandler 接管了 es 的操作,负责数据处理到数据写入
  2. 对上实现了 kafkaConsume 操作。这样在消费过程中执行 handler 的操作,从而写入 es

实际上,Consume() 也是这么处理的:

func (mh *MessageHandler) Consume(_, val string) error {
    var m map[string]interface{}
  // 反序列化从 kafka 中的消息
    if err := jsoniter.Unmarshal([]byte(val), &m); err != nil {
        return err
    }
    // es 写入index配置
    index := mh.indexer.GetIndex(m)
  // filter 链式处理【因为没有泛型,整个处理都是 `map进map出`】
    for _, proc := range mh.filters {
        if m = proc(m); m == nil {
            return nil
        }
    }
    bs, err := jsoniter.Marshal(m)
    if err != nil {
        return err
    }
    // es 写入
    return mh.writer.Write(index, string(bs))
}

数据流

说完了数据处理,以及上下游的连接点。但是数据要从 kafka -> es ,数据流出这个动作从 kafka 角度看,应该是由开发者主动 pull data from kafka

那么数据流是怎么动起来?我们回到主程序 https://github.com/tal-tech/go-stash/blob/master/stash/stash.go

其实 启动 整个流程中,其实就是一个组合模式:

func main() {
    // 解析命令行参数,启动优雅退出
    ...
  // service 组合模式
    group := service.NewServiceGroup()
    defer group.Stop()

    for _, processor := range c.Clusters {
        // 连接es
    ...
        // filter processors 构建
    ...
    // 准备es的写入操作 {写入的index, 写入器writer}
        handle := handler.NewHandler(writer, indexer)
        handle.AddFilters(filters...)
        handle.AddFilters(filter.AddUriFieldFilter("url", "uri"))
    // 按照配置启动kafka,并将消费操作传入,同时加入组合器
        for _, k := range toKqConf(processor.Input.Kafka) {
            group.Add(kq.MustNewQueue(k, handle))
        }
    }
    // 启动这个组合器
    group.Start()
}

整个数据流,就和这个 group 组合器有关了。

group.Start()
    |- group.doStart()
        |- [service.Start() for service in group.services]

那么说明加入 groupservice 都是实现 Start()。也就是说 kafka 端的启动逻辑在 Start()

func (q *kafkaQueue) Start() {
    q.startConsumers()
    q.startProducers()

    q.producerRoutines.Wait()
    close(q.channel)
    q.consumerRoutines.Wait()
}
  1. 启动 kafka 消费程序
  2. 启动 kafka 消费拉取端【可能会被名字迷惑,实际上是从 kafka 拉取消息到 q.channel
  3. 消费程序终止,收尾工作

而我们传入 kafka 中的 handler,上文说过其实是 Consume,而这个方法就是在 q.startConsumers() 中执行的:

q.startConsumers()
    |- [q.consumeOne(key, value) for msg in q.channel]
        |- q.handler.Consume(key, value)

这样整个数据流就彻底串起来了:

image.png

总结

作为 go-stash 第一篇文章,本篇从架构和设计上整体介绍 go-stash ,有关性能和为什么我们要开发一个这样的组件,我们下篇文章逐渐揭晓。

https://github.com/tal-tech/go-stash

关于 go-zero 更多的设计和实现文章,可以持续关注我们。

https://github.com/tal-tech/go-zero

欢迎使用 go-zero 并 star 支持我们!

微信交流群

关注『微服务实践』公众号并回复 进群 获取社区群二维码。

go-zero 系列文章见『微服务实践』公众号

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容