英文:
Running goroutine (scheduler) to refresh the global variable in AWS lambda
问题
我正在尝试通过在全局上下文中生成goroutine来刷新AWS Lambda中的全局变量,以刷新该变量。但是,每次调用Lambda时,连接字符串的值都相同。我做错了什么?goroutine在Lambda的全局上下文中不起作用吗?行为非常不一致,CloudWatch不显示后台作业日志,只有在触发事件时才显示。有人能告诉我在Lambda中实现相同效果的正确方法吗?
以下是代码:
var mx sync.Mutex
var token string
func refresh() {
fmt.Println("进行HTTP调用")
time.Sleep(3*time.Second)
fmt.Println("调用结束")
mx.Lock()
defer mx.Unlock()
token = "TEST" + strconv.FormatInt(int64(randInt(200)), 10)
schedule()
}
func schedule() {
time.AfterFunc(1*time.Minute, func() {
refresh()
})
}
func init() {
fmt.Println("进行HTTP调用")
time.Sleep(3*time.Second)
fmt.Println("调用结束")
mx.Lock()
token = "TEST1"
mx.Unlock()
go schedule()
return
}
func processEvent() {
fmt.Println("变量: %s\n", token)
return "Hello World", nil
}
func main() {
lambda.Start(processEvent)
}
希望这可以帮助你。
英文:
I'm trying to refresh the global variable in AWS lambda by spawning goroutine in global context which will refresh the variable. But every-time I'm invoking the lambda the I'm getting the same value for connection string. what I'm doing wrong ? Does the goroutine doesn't work in global context of lambda ? Behaviour is very inconsisten and cloud watch doesn't show the background jobs logs it shows only when I trigger event. Can anyone tell what is the right way to achieve the same in lambda ?
Here is the code
var mx sync.Mutex
var token string
func refresh() {
fmt.Println("Make HTTP Call")
time.Sleep(3*time.Second)
fmt.Println("Call Ends")
mx.Lock()
defer mx.Unlock()
token = "TEST" + strconv.FormatInt(int64(randInt(200), 10)
schedule()
}
func schedule() {
time.AfterFunc(1*time.Minute, func() {
refresh()
})
}
func init() {
fmt.Println("Make HTTP Call")
time.Sleep(3*time.Second)
fmt.Println("Call Ends")
mx.Lock()
token = "TEST1"
mx.Unlock()
go schedule()
return
}
function processEvent() {
fmt.Println("Variable: %s\n", token)
return "Hello World", nil
}
function main() {
lambda.Start(processEvent)
}
答案1
得分: 1
如果你想要使用定期运行的调度器,就不能使用time.AfterFunc
,它只会在指定的时间之后运行一次。如果你想要每分钟刷新令牌,你需要使用其他解决方案。
import "github.com/jasonlvhit/gocron"
init() {
gocron.Every(1).Minute().Do(refresh)
}
更新
也许我没有理解你的用例。但是你可能误解了AWS Lambda。它不是设计用来运行“永不停止的定期调度器”来定期更新令牌的。它的设计是在被调用时执行任务并返回结果,如果Lambda在一段时间内没有被使用(参考:一段时间),那么它会关闭。
这个解决方案还有另一个问题,Lambda不共享运行时上下文。Lambda“提供了一个安全和隔离的运行时环境”。所以,如果你需要在所有Lambda之间共享数据,你需要寻找其他解决方案,而不是运行时上下文,比如全局状态。你也不需要使用互斥锁,因为一次只有一个调用。参考Lambda生命周期。
你可以在使用令牌之前检查令牌的有效性,但这可能会降低性能。
package main
import (
"fmt"
"github.com/aws/aws-lambda-go/lambda"
"math/rand"
"strconv"
"time"
)
var token Token
type Token struct {
token string
valid time.Time
}
func newToken(token string) Token {
// 创建一个有效期为一分钟的新令牌
return Token{token, time.Now().Add(1 * time.Minute)}
}
func (t Token) isValid() bool {
return t.valid.After(time.Now())
}
func refresh() {
randToken := fetchToken()
token = newToken(randToken)
}
func init() {
rand.Seed(time.Now().UnixNano()) // 将种子放入rand
refresh()
}
func fetchToken() string {
// 实现你的逻辑
rnd := rand.Int()
return strconv.FormatInt(int64(rnd), 10)
}
func HandleRequest() (string, error) {
if !token.isValid() {
refresh() // 如果令牌无效,则刷新令牌
}
return fmt.Sprintf("Token: %s", token.token), nil
}
func main() {
lambda.Start(HandleRequest)
}
英文:
If you want to use scheduler which runs periodically you cannot use time.AfterFunc
it runs only once after specified time. If you want to refresh token every minute you need to use other solution
import "github.com/jasonlvhit/gocron"
init() {
gocron.Every(1).Minute().Do(refresh)
}
Update
Maybe I don't understand your use case. But you probably misunderstood the AWS Lambda. It is not designed to run "never ending scheduler" for updating token periodically. It is designed to do the job when it is invoked and return the result and if Lambda is not used any more for some period of time, then it shutdowns.
There is also another issue with this solution, Lambda do not share runtime context. Lambda provides a secure and isolated runtime environment
. So If you need share data across all Lambdas you need to look for other solution than runtime context, like global state. You also do not need to use Mutex because there is only one invoke at the time. See Lambda lifecycle
You can check validity of the token before using it, but it can slow down your performance.
package main
import (
"fmt"
"github.com/aws/aws-lambda-go/lambda"
"math/rand"
"strconv"
"time"
)
var token Token
type Token struct {
token string
valid time.Time
}
func newToken(token string) Token {
// create new token with validity one minute
return Token{token, time.Now().Add(1 * time.Minute)}
}
func (t Token) isValid() bool {
return t.valid.After(time.Now())
}
func refresh() {
randToken := fetchToken()
token = newToken(randToken)
}
func init() {
rand.Seed(time.Now().UnixNano()) // paste seed into rand
refresh()
}
func fetchToken() string {
// implement your logic
rnd := rand.Int()
return strconv.FormatInt(int64(rnd), 10)
}
func HandleRequest() (string, error) {
if !token.isValid() {
refresh() // refresh token if not valid
}
return fmt.Sprintf("Token: %s", token.token), nil
}
func main() {
lambda.Start(HandleRequest)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论