go語言心跳超時的實(shí)現(xiàn)示例
一、背景
本文描述的是客戶端接收心跳信息的超時實(shí)現(xiàn)。心跳超時,或者接受信息超過限定時間在分布式系統(tǒng)中出現(xiàn)的次數(shù)比較多。常見的就有hadoop中節(jié)點(diǎn)超時,或者日志中出現(xiàn)timeout的字樣。
在學(xué)習(xí)go語言中,我也根據(jù)go語言的機(jī)制實(shí)現(xiàn)了心跳超時的這個問題。踩過坑,趟過水。
二、心跳超時的實(shí)現(xiàn)
2.1 通過select case (設(shè)計概念比較多)
這種方法實(shí)現(xiàn)心跳,需要對go語言中的channel和select case 機(jī)制有所了解。select代碼段中沒有包含default條件時,會一直阻塞到有通道操作。
需要注意的是?。。?! select語言只會阻塞一次,且執(zhí)行一次。如果需要多次判斷,或者可能有多個case條件需要滿足,那就需要增加for語句。
首先需要知道的是select是專為channel設(shè)計的,所以說每個case表達(dá)式都必須是包含操作通道的表達(dá)式。下面這段代碼是描述了隨機(jī)抽取一個channel發(fā)消息,正常情況下,不會觸發(fā)超時。為了觸發(fā)超時,注釋掉通道發(fā)送數(shù)據(jù)操作。超時五秒,則觸發(fā)超時。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
// 準(zhǔn)備好三個通道。
intChannels := [3]chan int{
make(chan int, 1),
make(chan int, 1),
make(chan int, 1),
}
// 隨機(jī)選擇一個通道,并向它發(fā)送元素值。
index := rand.Intn(3)
fmt.Printf("The index: %d\n", index)
//?? 取消這行代碼的注視,超時條件的選擇就會觸發(fā)。
//intChannels[index] <- index
// 哪一個通道中有可取的元素值,哪個對應(yīng)的分支就會被執(zhí)行。
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()實(shí)現(xiàn)超時操作,是比較巧妙的。一般來說心跳超時是一個雙方交互的行為。
下面畫一個圖來描述一下。

為了方便理解,定義雙方都使用共同時間。
下面是代碼。
基本的邏輯是:
1、先給客戶端設(shè)置一個下次超時的時間
2、客戶端每次收到心跳的時候,更新這個時間
3、開啟一個獨(dú)立的線程,一致判斷當(dāng)前客戶端是否超時。
ps:結(jié)合時效和性能,可以間隔一定的時間來進(jìn)行判斷。
package main
import (
"fmt"
"sync"
"time"
)
type Client struct {
lock sync.Mutex //加鎖
nextTimeOutTime time.Time //下次超時時間
}
const tenSec = 10
/**
刷新每次的心跳超時機(jī)制
*/
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 請求發(fā)送時間\n", time.Now())
client.receiveHeart()
}
}
func main() {
client := Client{
lock: sync.Mutex{},
nextTimeOutTime: time.Time{},
}
//在當(dāng)前時刻,更新下次的超時時刻是10s中后
client.freshTimeOutTime()
go pingClient(&client)
go client.judgeTimeOut()
for true {
}
}
三、個人的實(shí)現(xiàn)觀感
使用select case 和 time.sleep實(shí)現(xiàn)超時的最大區(qū)別在于,time.sleep沒有太多的??語言相關(guān)的語法和知識,更容易理解和掌握。相對于channel來說,掌握需要了解channel的基本使用方法,一些常見的特性等。
到此這篇關(guān)于go語言心跳超時的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)go語言心跳超時內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go-zero熔斷機(jī)制組件Breaker接口定義使用解析
這篇文章主要為大家介紹了go-zero熔斷機(jī)制組件Breaker接口定義使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
利用go-kit組件進(jìn)行服務(wù)注冊與發(fā)現(xiàn)和健康檢查的操作
這篇文章主要介紹了利用go-kit組件進(jìn)行服務(wù)注冊與發(fā)現(xiàn)和健康檢查的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
Golang中的sync.WaitGroup用法實(shí)例
這篇文章主要介紹了Golang中的sync.WaitGroup用法實(shí)例,WaitGroup的用途,它能夠一直等到所有的goroutine執(zhí)行完成,并且阻塞主線程的執(zhí)行,直到所有的goroutine執(zhí)行完成,需要的朋友可以參考下2015-07-07

