golang 用Go编写的IRC bot,在RethinkDB集群遇到问题时提供通知

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang 用Go编写的IRC bot,在RethinkDB集群遇到问题时提供通知相关的知识,希望对你有一定的参考价值。

package main

import (
	"code.google.com/p/gcfg"
	"fmt"
	r "github.com/dancannon/gorethink"
	irc "github.com/fluffle/goirc/client"
	"log"
	"strings"
)

type Config struct {
	IRC struct{ Host, Channel, Nickname string }
	DB  struct{ Host string }
}

type Issue struct {
	Description, Type string
}

type Server struct {
	Name, Status string
}

func main() {
	quit := make(chan bool, 1)

	var config Config
	if err := gcfg.ReadFileInto(&config, "config.gcfg"); err != nil {
		log.Fatal("Couldn't read configuration")
	}

	db, err := r.Connect(r.ConnectOpts{Address: config.DB.Host})
	if err != nil {
		log.Fatal("Database connection failed:", err)
	}

	ircConf := irc.NewConfig(config.IRC.Nickname)
	ircConf.Server = config.IRC.Host
	bot := irc.Client(ircConf)

	bot.HandleFunc("connected", func(conn *irc.Conn, line *irc.Line) {
		log.Println("Connected to IRC server", config.IRC.Host)
		conn.Join(config.IRC.Channel)
	})

	bot.HandleFunc("privmsg", func(conn *irc.Conn, line *irc.Line) {
		log.Println("Received:", line.Nick, line.Text())
		if strings.HasPrefix(line.Text(), config.IRC.Nickname) {
			command := strings.Split(line.Text(), " ")[1]
			switch command {
			case "quit":
				log.Println("Received command to quit")
				quit <- true
			}
		}
	})

	log.Println("Connecting to IRC server", config.IRC.Host)
	if err := bot.Connect(); err != nil {
		log.Fatal("IRC connection failed:", err)
	}

	issues, _ := r.Db("rethinkdb").Table("current_issues").Filter(
		r.Row.Field("critical").Eq(true)).Changes().Field("new_val").Run(db)

	go func() {
		var issue Issue
		for issues.Next(&issue) {
			if issue.Type != "" {
				text := strings.Split(issue.Description, "\n")[0]
				message := fmt.Sprintf("(%s) %s ...", issue.Type, text)
				bot.Privmsg(config.IRC.Channel, message)
			}
		}
	}()

	<-quit
}

用大片字符串理解Golang内存管理

我正在为Go编写的Twitch.tv网站上的聊天机器人。

机器人的一个特征是奖励用户观看特定流的积分系统。此数据存储在SQLite3数据库中。

为了获得观众,机器人进行API调用以抽搐并收集流的所有当前观众。然后将这些观众放入一串字符串中。

总观看者的范围可以从几个到20,000或更多。

机器人做了什么

  • 进行API调用
  • 将所有观看者存储在一个字符串中
  • 对于每个观看者,bot会相应地迭代并添加点。
  • Bot在下一次迭代之前清除此切片

type Viewers struct {
    Chatters struct {
        CurrentModerators []string `json:"moderators"`
        CurrentViewers    []string `json:"viewers"`
    } `json:"chatters"`
}    

func RunPoints(timer time.Duration, modifier int, conn net.Conn, channel string) {
    database := InitializeDB() // Loads database through SQLite3 driver
    var Points int
    var allUsers []string
    for range time.NewTicker(timer * time.Second).C {
        currentUsers := GetViewers(conn, channel)
        tx, err := database.Begin()
        if err != nil {
            fmt.Println("Error starting points transaction: ", err)
        }

        allUsers = append(allUsers, currentUsers.Chatters.CurrentViewers...)
        allUsers = append(allUsers, currentUsers.Chatters.CurrentModerators...)

        for _, v := range allUsers {
            userCheck := UserInDB(database, v)
            if userCheck == false {
                statement, _ := tx.Prepare("INSERT INTO points (Username, Points) VALUES (?, ?)")
                statement.Exec(v, 1)
            } else {

                err = tx.QueryRow("Select Points FROM points WHERE Username = ?", v).Scan(&Points)
                if err != nil {

                } else {
                    Points = Points + modifier
                    statement, _ := tx.Prepare("UPDATE points SET Points = ? WHERE username = ?")
                    statement.Exec(Points, v)
                }
            }
        }

        tx.Commit()
        allUsers = allUsers[:0]
        currentUsers = Viewers{} // Clear Viewer object struct

    }

预期的行为

当吸引成千上万的观众时,我自然会期望系统资源变得非常高。这可以使用3.0 MB的RAM将机器人转为20 MB +。当然,成千上万的元素占用了大量的空间!

然而,还有其他事情发生。

实际行为

每次调用API时,RAM都会按预期增加。但是因为我清除了切片,我预计它会回落到它的“正常”3.0 MB的使用量。

但是,每次API调用的RAM使用量会增加,即使流的查看者总数有所增加,也不会降低。

因此,给了几个小时,机器人将很容易消耗100 + MB的ram,这对我来说似乎不对。


我在这里错过了什么?我对编程和CS一般都是新手,所以也许我正在尝试修复一些不是问题的东西。但这几乎听起来像是一个内存泄漏给我。

我已经尝试通过Golang的运行时库强制垃圾收集和释放内存,但这并没有解决它。

答案

要了解这里发生了什么,您需要了解切片的内部结构以及发生了什么。你应该从https://blog.golang.org/go-slices-usage-and-internals开始

简单回答:切片提供了对底层数组的一部分的视图,当您尝试截断切片时,您所做的只是减少对数组的视图,但底层数组不受影响并且仍占用同样多的内存。实际上,通过继续使用相同的数组,您永远不会减少您正在使用的内存量。

我鼓励你阅读它是如何工作的,但作为为什么没有释放实际内存的例子,看看这个简单程序的输出,该程序演示了如何更改切片不会截断分配的内存引擎盖下:https://play.golang.org/p/PLEZba8uD-L

另一答案

重新切片时:

allUsers = allUsers[:0]

所有元素仍在后备阵列中,无法收集。内存仍然被分配,这将在下一次运行中节省一些时间(它不必大量调整数组大小,节省了缓慢的分配),这是将其重新调整为零长度而不是仅仅转储它的点。

如果您希望将内存释放到GC,则需要完全转储它并每次创建一个新的切片。这会更慢,但在运行之间使用更少的内存。但是,这并不一定意味着您将看到该进程使用的内存更少。 GC收集未使用的堆对象,然后可能最终将该内存释放到操作系统,如果其他进程正在施加内存压力,操作系统最终可能会回收它。

以上是关于golang 用Go编写的IRC bot,在RethinkDB集群遇到问题时提供通知的主要内容,如果未能解决你的问题,请参考以下文章

Twisted Python IRC bot - 如何异步执行函数以便它不会阻塞机器人?

用大片字符串理解Golang内存管理

Python IRC bot 在 3 次 ping 后断开连接

Python IRC bot + SQLAlchemy - 多渠道跟踪用户

带SSL的简单IRC bot

Irc-bot 使用扭曲与期刊印刷