Golang在GAE平台上开发的Telegram机器人运行一段时间后停止服务的问题

Golang在GAE平台上开发的Telegram机器人运行一段时间后停止服务的问题 Telegram 机器人在运行一段时间后停止工作。

我将一个简单的 Telegram 机器人上传到 Google 应用引擎。上传后它能正常工作一段时间,但之后就会停止响应 Telegram 的请求。这个机器人非常简单,使用了 github.com/go-telegram-bot-api/telegram-bot-api 包。

func main() {
    bot, err := tba.NewBotAPI("apitockenhere")
    var ucfg tba.UpdateConfig = tba.NewUpdate(0)
    ucfg.Timeout = 60

    uchan, err := bot.GetUpdatesChan(ucfg)
    if err != nil {
	log.Println(err)
    }
    ...
    for {
        select {
        case update := <-uchan:
        ...
        }
    }
}

如果我在浏览器中打开这个网络应用(它不包含任何处理程序,所以不会显示任何内容),Telegram 机器人会重新开始工作一段时间,然后再次停止。

在本地环境中它能正常工作,但在 GAE 上可能存在一些问题。 如何修改代码来防止它停止运行?

我注意到如果关闭浏览器,过一段时间后 Telegram 机器人也会停止工作。


更多关于Golang在GAE平台上开发的Telegram机器人运行一段时间后停止服务的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

也许,请求无法通过防火墙。

更多关于Golang在GAE平台上开发的Telegram机器人运行一段时间后停止服务的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


好的,如果你已经得到答案,请随时"标记为已解决"。我会选择选项3,因为搭建一个小型机器人要容易得多。

husonet:

也许,请求无法通过防火墙

我怀疑是防火墙的问题。发帖人特别提到,通过浏览器访问机器人以及上传后,机器人都能正常工作。

我确实期望GAE有某种机制来防止机器人行为,就像Heroku一样。

hollowaykeanho:

当通过浏览器访问机器人并上传后,机器人运行正常

确实如此

在GAE上运行Telegram机器人时遇到服务停止的问题,通常是由于GAE的实例管理机制导致的。GAE会自动停止闲置实例以节省资源,而你的长轮询连接在实例被挂起时会中断。

以下是解决方案和修改后的代码:

问题分析

  1. GAE实例在无请求时会自动挂起
  2. 长轮询连接在实例挂起时被中断
  3. 需要保持实例活跃或使用Webhook方式

解决方案:使用Webhook替代长轮询

package main

import (
    "log"
    "net/http"
    "os"
    
    tba "github.com/go-telegram-bot-api/telegram-bot-api"
)

var bot *tba.BotAPI

func main() {
    var err error
    bot, err = tba.NewBotAPI(os.Getenv("TELEGRAM_APITOKEN"))
    if err != nil {
        log.Fatal(err)
    }

    // 设置Webhook
    _, err = bot.SetWebhook(tba.NewWebhook("https://your-app-name.appspot.com/"+bot.Token))
    if err != nil {
        log.Fatal(err)
    }

    // 设置路由
    http.HandleFunc("/", handleRoot)
    http.HandleFunc("/"+bot.Token, handleWebhook)

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    log.Printf("Server starting on port %s", port)
    log.Fatal(http.ListenAndServe(":"+port, nil))
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Telegram Bot is running"))
}

func handleWebhook(w http.ResponseWriter, r *http.Request) {
    update, err := bot.HandleUpdate(r)
    if err != nil {
        log.Printf("Error handling update: %v", err)
        return
    }

    if update.Message != nil {
        handleMessage(update.Message)
    }
}

func handleMessage(message *tba.Message) {
    msg := tba.NewMessage(message.Chat.ID, "Received: "+message.Text)
    bot.Send(msg)
}

替代方案:保持实例活跃(如果必须使用长轮询)

package main

import (
    "log"
    "net/http"
    "time"
    
    tba "github.com/go-telegram-bot-api/telegram-bot-api"
)

func main() {
    // 启动健康检查端点
    http.HandleFunc("/_ah/health", healthCheckHandler)
    go http.ListenAndServe(":8080", nil)

    bot, err := tba.NewBotAPI("apitockenhere")
    if err != nil {
        log.Fatal(err)
    }

    var ucfg tba.UpdateConfig = tba.NewUpdate(0)
    ucfg.Timeout = 60

    uchan, err := bot.GetUpdatesChan(ucfg)
    if err != nil {
        log.Fatal(err)
    }

    // 定期发送请求保持实例活跃
    go keepAlive()

    for update := range uchan {
        if update.Message != nil {
            handleUpdate(&update)
        }
    }
}

func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("ok"))
}

func keepAlive() {
    ticker := time.NewTicker(5 * time.Minute)
    defer ticker.Stop()
    
    for range ticker.C {
        // 发送一个简单的HTTP请求到应用自身
        http.Get("https://your-app-name.appspot.com/_ah/health")
    }
}

func handleUpdate(update *tba.Update) {
    // 处理消息逻辑
    log.Printf("Received message: %s", update.Message.Text)
}

app.yaml配置示例

runtime: go116
instance_class: F2
automatic_scaling:
  max_instances: 1
  min_instances: 0

Webhook方案是推荐的方法,因为它符合GAE的无状态请求-响应模型,能有效避免实例挂起导致的服务中断问题。

回到顶部