前情回顾

前文我们完成了如下功能
1 根据配置文件启动多个协程监控日志,并启动协程监听配置文件。
2 根据配置文件热更新,动态协调日志监控。
3 编写测试代码,向文件中不断写入日志并备份日志,验证系统健壮性。

本节目标

我们旨在编写一个健壮性较强的日志监控系统,不得不考虑这样一个问题,当某个日志监控协程崩溃或者异常退出,该如何处理?
我的想法是当监控日志文件的协程异常退出时,在主协程复活这个异常退出的协程,使其继续工作,这样极大的增强了系统的稳定性。

借尸还魂,增加异常处理

我们在tailf.go的WatchLogFile中增加异常处理,在协程崩溃时打印日志信息,并且向keychan中写入字符串通知主协程处理。

func WatchLogFile(pathkey string, datapath string, ctx context.Context, keychan chan<- string) {
//省略之前写好的逻辑
//....
defer func() {
if errcover := recover(); errcover != nil {
fmt.Println("goroutine watch ", pathkey, " panic")
fmt.Println(errcover)
keychan <- pathkey
}
}()
//省略逻辑....
}

WatchLogFile函数进行了扩充,增加了pathkey字符串表示监控哪个日志文件,pathkey实际是config.yaml中的路径的key值。
keychan 实际是一个缓冲chan,用来和主协程通信,告诉自己挂掉了。
WatchLogFile 中其余逻辑和前文一样,不做赘述。

主协程中增加复活逻辑

同样,在主协程中我们需要创建keychan这个缓冲chan,并且捕获子协程发过来的崩溃消息。

func main(){
//....省略
keyChan := make(chan string, KEYCHANSIZE)
//....
for {
select {
case pathData, ok := <-pathChan:
//省略...
case keystr := <-keyChan:
val, ok := configMgr[keystr]
if !ok {
continue
}
fmt.Println("recover goroutine watch ", keystr)
var ctxcover context.Context
ctxcover, val.ConfigCancel = context.WithCancel(context.Background())
go logtailf.WatchLogFile(keystr, val.ConfigValue,
ctxcover, keyChan)
}
}
}

在主协程中愿有逻辑基础上,我们增加了keyChan的初始化,以及keychan数据的监听。
当主协程收到keychan的数据时,我们可以根据keystr修改其对应的context,并且启动新的协程继续监听该日志文件。
这样就达到了复活那些异常死掉的协程的目的。

keychan该如何回收

keychan被多个子协程引用,该如何回收?这种情况下多个子协程写数据,一个主协程接受数据,我们常用的策略如下:
1 不要在接受协程中关闭chan,因为如果此时有其他发送协程向关闭的chan写数据会导致崩溃。
2 有多个发送协程,等待最后一个发送协程退出时关闭chan可防止崩溃。
3 不作处理,等待系统回收,前提是保证所有协程正常退出,否则会导致资源泄漏。
我再三考虑了一下,统计最后协程退出会增加逻辑的复杂性,所以交给系统回收吧,但是我做好了协程的死锁检测和退出通知,应该不会有问题。

制造协程崩溃现场,模拟测试系统稳定性

为了测试我们的系统稳定性,我在修改WatchLogFile函数,新增如下处理,中断key值为logdir3的监控协程,进而观察主协程能否再次启动协程监听该日志文件。

func WatchLogFile(pathkey string, datapath string, ctx context.Context, keychan chan<- string) {
//...省略
defer func() {
if errcover := recover(); errcover != nil {
fmt.Println("goroutine watch ", pathkey, " panic")
fmt.Println(errcover)
keychan <- pathkey
}
}() //模拟崩溃
if pathkey == "logdir3" {
panic("test panic ")
}
//...省略

然后我们启动日志系统,看到如下效果

从日志上可以看到每当我们的协程挂掉,主协程会启动新的协程继续监听日志,保证了系统的稳定性。
谢谢关注我的公众号

源码下载地址
https://github.com/secondtonone1/golang-/tree/master/logcatchsys

Go项目实战:打造高并发日志采集系统(五)的更多相关文章

  1. Go项目实战:打造高并发日志采集系统(一)

    项目结构 本系列文章意在记录如何搭建一个高可用的日志采集系统,实际项目中会有多个日志文件分布在服务器各个文件夹,这些日志记录了不同的功能.随着业务的增多,日志文件也再增多,企业中常常需要实现一个独立的 ...

  2. Go项目实战:打造高并发日志采集系统(六)

    前情回顾 前文我们完成了日志采集系统的日志文件监控,配置文件热更新,协程异常检测和保活机制. 本节目标 本节加入kafka消息队列,kafka前文也介绍过了,可以对消息进行排队,解耦合和流量控制的作用 ...

  3. Go项目实战:打造高并发日志采集系统(二)

    日志统计系统的整体思路就是监控各个文件夹下的日志,实时获取日志写入内容并写入kafka队列,写入kafka队列可以在高并发时排队,而且达到了逻辑解耦合的目的.然后从kafka队列中读出数据,根据实际需 ...

  4. Go项目实战:打造高并发日志采集系统(三)

    前文中已经完成了文件的监控,kafka信息读写,今天主要完成配置文件的读写以及热更新.并且规划一下系统的整体结构,然后将之前的功能串起来形成一套完整的日志采集系统. 前情提要 上一节我们完成了如下目标 ...

  5. Go项目实战:打造高并发日志采集系统(四)

    前情回顾 前文我们完成了如下目标1 项目架构整体编写2 使框架支持热更新 本节目标 在前文的框架基础上,我们1 将之前实现的日志监控功能整合到框架中.2 一个日志对应一个监控协程,当配置热更新后根据新 ...

  6. 《实战java高并发程序设计》源码整理及读书笔记

    日常啰嗦 不要被标题吓到,虽然书籍是<实战java高并发程序设计>,但是这篇文章不会讲高并发.线程安全.锁啊这些比较恼人的知识点,甚至都不会谈相关的技术,只是写一写本人的一点读书感受,顺便 ...

  7. 《实战Java高并发程序设计》读书笔记

    文章目录 第二章 Java并行程序基础 2.1 线程的基本操作 2.1.1 线程中断 2.1.2 等待(wait)和通知(notify) 2.1.3 等待线程结束(join)和谦让(yield) 2. ...

  8. 【实战Java高并发程序设计 7】让线程之间互相帮助--SynchronousQueue的实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  9. 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

随机推荐

  1. 【CQOI2017】老C的方块

    Description https://loj.ac/problem/3022 Solution 他讲得很清楚 将那篇博客中的红色标号为 \(0\),黄色为 \(1\),蓝色为 \(2\),绿色为 \ ...

  2. SCU 4442 party 二分图最大点权独立集

    每个青蛙喝黑茶或者红茶或者都可以喝 M个矛盾关系 有矛盾的不能喝同种茶 但你可以花费Wi使得这个青蛙消除所有矛盾 把矛盾当作边 青蛙当作点 如果这两个青蛙只喝不同的一种茶就不建边 题目中保证了不存在奇 ...

  3. PAT乙级1037

    题目链接 https://pintia.cn/problem-sets/994805260223102976/problems/994805284923359232 题解 还算简单,就是模拟我们在生活 ...

  4. C语言之volatile

    emOsprey  鱼鹰谈单片机 2月21日 预计阅读时间: 4 分钟 和 const 不同(关于 const 可以看 const 小节),当一个变量声明为 volatile,说明这个变量会被意想不到 ...

  5. springboot2.0入门(一)----springboot 简介

    一.springboot解决了什么? 避免了繁杂的xml配置,框架自动帮我们完成了相关的配置,当我们需要进行相关插件集成的时候,只需要将相关的starter通过相关的maven依赖引进,并可以进行相关 ...

  6. 10 masterless、高可用、salt执行模块开发、sydic架构

    1.salt无master 官方文档: http://docs.saltstack.cn/topics/tutorials/quickstart.html 1.使用场景 1.在项目中使用salt,写一 ...

  7. 【Android-自定义控件】 漂亮的Toast

    修改Toast属性,美化Toast //创建一个Toast Toast toast=new Toast(getApplicationContext()); //创建Toast中的文字 TextView ...

  8. zuul网管配置其他服务时 HTTP Status 500 – Internal Server Error

    1.这个错误是由三个原因导致的 (1).我在给类的接口命名的时候前面少加了一个/ (2)给zuul配置路由的时候多加了个服务名,serviceId名称就是spring的name,而不是eureka注入 ...

  9. MFC 画字体DrawText()或TextOut(),CFont字体样式类

    新建单个文档的MFC应用程序,类视图——View项的属性——消息,WM_PAINT,创建OnPaint()函数 void CMFCApplication27View::OnPaint() { CPai ...

  10. contents() 查找匹配元素内部所有的子节点(包括文本节点)。如果元素是一个iframe,则查找文档内容

    contents() V1.2概述 查找匹配元素内部所有的子节点(包括文本节点).如果元素是一个iframe,则查找文档内容   示例 描述:大理石平台检定规程 查找所有文本节点并加粗 HTML 代码 ...