GOLANG实现超时对象检测的最好理解的方式

原文:https://gocn.io/article/328

依赖于心跳的系统,都需要超时检测。比如P2P系统中客户端每隔120秒向数据服务器发送一次数据汇总,服务器就需要维护一个超时时间。比如一个UDP服务器,在和客户端之间创建Session之后,如果没有数据包,一般会有Ping包,说明这个Session是存活的,服务器在发现Session超时后也需要清理。

首先,服务器一般需要维护一个列表,以Peer为例:

type Peer struct {
    id uint64
    heartbeat time.Time
}

type Server struct {
    peers map[uint64]*Peer
    lock sync.Mutex
}

创建Peer,同时在收到Ping消息后,更新Peer的心跳时间:

func (v *Server) Create(id uint64) *Peer {
    v.lock.Lock()
    defer v.lock.UnLock()

    p = &Peer { id:id, heartbeat: time.Now(), }
    v.peers[id] = p
    return p
}

func (v *Server) OnPing(id uint64) {
    v.lock.Lock()
    defer v.lock.UnLock()

    if p,ok := v.peers[id]; ok {
        p.heatbeat = time.Now()
    }
}

当然,需要起一个goroutine定期扫描这个列表, 假设300秒超时:

go func(v *Server) {
    for {
        func(){
            v.lock.Lock()
            defer v.lock.UnLock()

            now := time.Now()
            for id,p := range v.peers {
                if p.heartbeat.Add(300 * time.Second).Before(now) {
                    delete(v.peers, id)
                }
            }
        }()
        time.Sleep(30 * time.Second)
    }
}(server)

如果Peers的数目非常多,那么扫描时每次都需要锁定v.peers,会导致其他的业务都无法进行。特别是清理Peer这个过程如果比较复杂,譬如需要发起io请求,是一个费时的操作时,就会造成系统的等待。

一般来说,超时的Peer不会很多,因此可以用chan放一个超时的peer,每个peer专门起一个goroutine来看什么时候超时,这样就可以在检测超时时避免用锁了:

timeout := make(chan *Peer)

func (v *Server) Create(id uint64) *Peer {
    v.lock.Lock()
    defer v.lock.UnLock()

    p = &Peer { id:id, heartbeat: time.Now(), }
    v.peers[id] = p
    return p

    go func(p *Peer) {
        for {
            tm := p.heartbeat
            <- time.After(300 * time.Second)
            if tm.Equal(p.heartbeat) {
                timeout <- p
                break
            }
        }
    }(p)
}

go func(v *Server){
    for gw := range timeout {
        func(){
            lgateways.Lock()
            defer lgateways.Unlock()

            delete(gateways, gw.port)
        }()

        // Do something cleanup about the gateway.
    }
}(server)

这样就只有在有Peer超时时,才真正锁住Server.peers

Golang是一种编程语言,具备强大的接口特性。在Golang中,接口是一种规定了对象必须遵循的行为规范。通过接口,我们可以定义一组方法的集合,任何实现了这些方法的类型都被视为实现了该接口。 接口的实现是指一个类型声明自己遵循了某个接口。实现一个接口的类型,需要实现接口中定义的所有方法。实现接口的类型不需要显式声明实现了该接口,只要实现了接口中定义的方法即可。这种方式称为隐式接口实现。 接口对象是指一个接口类型的变量或参数,可以指向满足该接口的任何具体类型的对象。通过接口对象,我们可以调用接口中定义的方法,而不需要关心具体对象是哪种类型。 使用接口的好处是可以将接口作为一个统一的契约,使得不同类型的对象可以通过实现相同的接口来实现类型的替换和灵活的设计。接口可以实现代码的重用,并且松耦合,提高代码的可维护性和可测试性。 在实际应用中,我们可以先定义接口,然后根据接口的行为规范来编写具体类型的实现。接口对象可以根据需要替换为不同的具体类型对象实现了面向接口编程的思想。 总结来说,Golang中的接口实现是指类型声明自己遵循了某个接口并实现了接口中定义的方法。接口对象是指可以指向满足该接口的任何具体类型的对象。通过接口,我们可以实现代码的灵活和重用,使得不同类型的对象可以通过实现相同的接口来进行类型的替换。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

winlinvip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值