go语言 心跳超时的实现go 超时

一、背景

本文描述的是客户端接收心跳信息的超时实现。心跳超时,或者接受信息超过限定时间在分布式系统中出现的次数比较多。常见的就有hadoop中节点超时,或者日志中出现timeout的字样。

在学习go语言中,我也根据go语言的机制实现了心跳超时的这个问题。踩过坑,趟过水。

二、心跳超时的实现

2.1 通过select case (设计概念比较多)

这种方法实现心跳,需要对go语言中的channel和select case 机制有所了解。select代码段中没有包含default条件时,会一直阻塞到有通道操作。

需要注意的是!!!! select语言只会阻塞一次,且执行一次。如果需要多次判断,或者可能有多个case条件需要满足,那就需要增加for语句。

首先需要知道的是select是专为channel设计的,所以说每个case表达式都必须是包含操作通道的表达式。下面这段代码是描述了随机抽取一个channel发消息,正常情况下,不会触发超时。为了触发超时,注释掉通道发送数据操作。超时五秒,则触发超时。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	// 准备好三个通道。
	intChannels := [3]chan int{
		make(chan int, 1),
		make(chan int, 1),
		make(chan int, 1),
	}
	// 随机选择一个通道,并向它发送元素值。
	index := rand.Intn(3)
	fmt.Printf("The index: %d\n", index)

    //‼️ 取消这行代码的注视,超时条件的选择就会触发。
	//intChannels[index] <- index
	// 哪一个通道中有可取的元素值,哪个对应的分支就会被执行。
	select {
	case <-intChannels[0]:
		fmt.Println("The first candidate case is selected.")
	case <-intChannels[1]:
		fmt.Println("The second candidate case is selected.")
	case elem := <-intChannels[2]:
		fmt.Printf("The third candidate case is selected, the element is %d.\n", elem)
	case <-time.After(5 * time.Second):
		fmt.Println("timed out")
	}
}
 

2.2 通过time.sleep(简单有效)

通过time.sleep()实现超时操作,是比较巧妙的。一般来说心跳超时是一个双方交互的行为。

下面画一个图来描述一下。

 为了方便理解,定义双方都使用共同时间。

下面是代码。

基本的逻辑是:

        1、先给客户端设置一个下次超时的时间

         2、客户端每次收到心跳的时候,更新这个时间

         3、开启一个独立的线程,一致判断当前客户端是否超时。

ps:结合时效和性能,可以间隔一定的时间来进行判断。

package main

import (
	"fmt"
	"sync"
	"time"
)

type Client struct {
	lock sync.Mutex //加锁
	nextTimeOutTime time.Time //下次超时时间
}

const tenSec = 10
/**
刷新每次的心跳超时机制
 */
func (client *Client) freshTimeOutTime()  {
	client.lock.Lock()
	defer client.lock.Unlock()
	client.nextTimeOutTime =time.Now().Add(tenSec*time.Second)
}

//开启一个gp,每隔500ms判断有没有超时
func (client *Client) judgeTimeOut()  {
	for  {
		time.Sleep(500*time.Millisecond)
		fmt.Printf("%v 在判断是否超时\n", client.nextTimeOutTime)
		if time.Now().After(client.nextTimeOutTime) {
			fmt.Printf("%v 超时了\n", client.nextTimeOutTime)
		}
	}
}

//客户端收到以后,修改下次心跳超时时间
func (client *Client) receiveHeart()  {
	client.freshTimeOutTime()
}

//开启一个模拟ping 客户端的线程
func pingClient(client *Client)  {
	for true {
		time.Sleep(11*time.Second)
		fmt.Printf("%v 请求发送时间\n", time.Now())
		client.receiveHeart()
	}

}

func main() {
	client := Client{
		lock:            sync.Mutex{},
		nextTimeOutTime: time.Time{},
	}
	//在当前时刻,更新下次的超时时刻是10s中后
	client.freshTimeOutTime()


	go pingClient(&client)


	go client.judgeTimeOut()

	for true {

	}
}

三、个人的实现观感

        使用select case 和 time.sleep实现超时的最大区别在于,time.sleep没有太多的🐶语言相关的语法和知识,更容易理解和掌握。相对于channel来说,掌握需要了解channel的基本使用方法,一些常见的特性等。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大锤爱编程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值