为了账号安全,请及时绑定邮箱和手机立即绑定

如何同步常量写入和周期性读取和更新

如何同步常量写入和周期性读取和更新

Go
蝴蝶不菲 2022-05-18 10:13:38
定义问题:我们有这个物联网设备,每个设备都会向我们发送有关汽车位置的日志。我们要计算汽车在线行驶的距离!因此,当日志出现时(将其放入队列等之后),我们会这样做:type Delta struct {    DeviceId string    time     int64    Distance float64}var LastLogs = make(map[string]FullLog)var Distances = make(map[string]Delta)func addLastLog(l FullLog) {    LastLogs[l.DeviceID] = l}func AddToLogPerDay(l FullLog) {    //mutex.Lock()    if val, ok := LastLogs[l.DeviceID]; ok {        if distance, exist := Distances[l.DeviceID]; exist {            x := computingDistance(val, l)            Distances[l.DeviceID] = Delta{                DeviceId: l.DeviceID,                time:     distance.time + 1,                Distance: distance.Distance + x,            }        } else {            Distances[l.DeviceID] = Delta{                DeviceId: l.DeviceID,                time:     1,                Distance: 0,            }        }    }    addLastLog(l)}它基本上使用效用函数计算距离!因此,在Distances每个设备中,ID 都映射到了一定距离!现在是问题开始的地方:虽然这个距离被添加到Distances map,我想要一个 go 例程来把这个数据放在数据库中,但是因为有很多设备和很多日志等等,对每个日志进行这个查询并不是一个好主意. 所以我需要每 5 秒执行一次,这意味着每 5 秒尝试清空添加到地图的所有最后距离的列表。我写了这个函数:func UpdateLogPerDayTable() {    for {        for _, distance := range Distances {            logs := model.HourPerDay{}            result := services.CarDBProvider.DB.Table(model.HourPerDay{}.TableName()).    }}它go utlis.UpdateLogPerDayTable()在另一个 goroutine 上被调用。但是这里有很多问题:我不知道如何保护Distances,所以当我将它添加到另一个例程中时,我在其他地方阅读它,一切都很好!(问题是我想使用 go 频道并且不知道该怎么做)对于这个问题,我如何安排任务?可能我会添加一个 redis 来存储所有在线设备,这样我就可以更快地进行选择查询并更新实际的数据库。还为 redis 添加过期时间,因此如果设备在一段时间内没有发送和数据,它就会消失!我应该把这段代码放在哪里?对不起,如果我的解释还不够,但我真的需要一些帮助。专门用于代码实现
查看完整描述

1 回答

?
慕容3067478

TA贡献1773条经验 获得超3个赞

Go 有一个非常酷的模式,在多个通道上使用for / select 。这允许您使用超时和最大记录大小来批量写入距离。使用这种模式需要使用通道。


首先是将您的距离建模为通道:


distances := make(chan Delta)

然后你跟踪当前批次


var deltas []Delta

然后


ticker := time.NewTicker(time.Second * 5)


var deltas []Delta


for {

  select {

     case <-ticker.C:

        // 5 seconds up flush to db

        // reset deltas

     case d := <-distances:

        deltas = append(deltas, d)

        if len(deltas) >= maxDeltasPerFlush {

           // flush

           // reset deltas

        }

  }

}

我不知道如何确保距离,所以当我在另一个例程中添加它时,我在其他地方阅读它,一切都很好!(问题是我想使用 go 频道并且不知道该怎么做)


如果您打算保留地图并共享内存,则需要使用互斥(互斥)来保护它,以同步 go 例程之间的访问。使用通道允许您将副本发送到通道,从而无需跨 Delta 对象进行同步。根据您的架构,您还可以创建由通道连接的 go 例程管道,这可以使其只有一个 go 例程(monitor goroutine)正在访问Delta,也无需同步。


对于这个问题,我如何安排任务?


使用通道作为传递Deltas给不同 go 例程的原语 :)


可能我会添加一个 redis 来存储所有在线设备,这样我就可以更快地进行选择查询并更新实际的数据库。还为 redis 添加过期时间,因此如果设备在一段时间内没有发送和数据,它就会消失!我应该把这段代码放在哪里?


这取决于您完成的架构。您可以为 select 操作编写一个装饰器,它会先检查 redis,然后再访问数据库。此功能的客户端不必知道这一点。写操作可以以相同的方式完成:写入持久存储,然后使用缓存值和过期时间写回 redis。使用装饰器,客户端不需要知道这一点,他们只需执行读取和写入,缓存逻辑将在装饰器内部实现。有很多方法可以做到这一点,这在很大程度上取决于您的实现在哪里解决。


查看完整回答
反对 回复 2022-05-18
  • 1 回答
  • 0 关注
  • 159 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信