Go 实现心跳

Go 实现心跳

心跳最典型的应用场景是是探测服务是否存活,比如在 Zookeeper 中,会使用心跳探测服务是否存货,如果服务已经死亡,会将服务从注册表中删除,避免服务请求路由到一个已经宕机的服务中。

Go 中实现心跳机制可以通过 time.NewTimeTicker(), 配合 channel 使用,就可以实现一个简单的心跳程序:

go 复制代码
import (
	"code.byted.org/gopkg/logs"
	"context"
	"fmt"
	"testing"
	"time"
)

func sendHeartbeat(heartbeatChan chan<- time.Time) {
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()
	for {
		select {
		case t := <-ticker.C:
			heartbeatChan <- t
		}
	}
}

func TestHeartbeat(t *testing.T) {

	heartbeatChan := make(chan time.Time)
	go doWork([]int{1, 2, 3, 5, 5}, heartbeatChan)
	for t := range heartbeatChan {
		// 上报心跳包的逻辑,可以在这里实现
		fmt.Println("Received heartbeat at", t)
	}
    fmt.Println("heartbeat finished")
}

func doWork(nums []int, heartbeatChan chan time.Time) {
	go sendHeartbeat(heartbeatChan)
	defer func() {
		close(heartbeatChan)
	}()
	for num := range nums {
        time.Sleep(1 * time.Second)
		fmt.Println(num)
	}
}

执行结果如下:

go 复制代码
=== RUN   TestHeartbeat
0
Received heartbeat at 2024-01-31 11:30:46.3363 +0800 CST m=+1.002830252
Received heartbeat at 2024-01-31 11:30:47.335975 +0800 CST m=+2.002513164
1
Received heartbeat at 2024-01-31 11:30:48.336252 +0800 CST m=+3.002795914
2
Received heartbeat at 2024-01-31 11:30:49.33622 +0800 CST m=+4.002768883
3
Received heartbeat at 2024-01-31 11:30:50.336222 +0800 CST m=+5.002775378
4
heartbeat finished
--- PASS: TestHeartbeat (5.00s)
PASS

心跳程序

客户端发送心跳请求, 并通过重试机制。判断重试X次失败认为服务离线 服务端响应心跳请求,通过超时机制。超时X秒未收到心跳则判断客户端离线

go 复制代码
package main

import (
	"fmt"
	"net"
	"time"
)

func main() {
	// 启动服务端
	go startServer()

	// 启动客户端
	go startClient()

	// 保持 main goroutine 活跃,避免程序退出
	select {}
}

func startServer() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer ln.Close()

	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}
		go handleConnection(conn)
	}
}

func handleConnection(conn net.Conn) {
	// 使用 bufio 包中的 ReadWriter 类型,方便读写字符串
	rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))

	// 启动一个 goroutine,定时发送心跳消息
	go func() {
		for {
			time.Sleep(time.Second)
			if _, err := rw.WriteString("heartbeat\n"); err != nil {
				fmt.Println(err)
				return
			}
			if err := rw.Flush(); err != nil {
				fmt.Println(err)
				return
			}
		}
	}()

	// 循环读取客户端发送的消息
	for {
		line, err := rw.ReadString('\n')
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println("received:", line)
	}
}

func startClient() {
	conn, err := net.Dial("tcp", "localhost:8080")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer conn.Close()

	//
}

参考资料

相关推荐
逍遥德几秒前
PostgreSQL --- 自增主键【序列】的避坑指南
数据库·后端·sql·mysql·postgresql·sqlserver
段ヤシ.7 分钟前
【Java框架】知识点汇总Day7:Spring Boot +Vue(持续更新)
vue.js·spring boot·后端·框架
土狗TuGou7 分钟前
SQL进阶笔记 · 第1篇:存储引擎
java·数据库·笔记·后端·sql·mysql
码语智行15 分钟前
Spring Security自定义AuthenticationManager实现手机号/密码双认证
java·后端·spring
武子康25 分钟前
Build-Your-Own-X 从零构建轻量级事件驱动微框架:嵌入式与物联网场景下的极简实践
人工智能·后端·物联网·ai·c#·大模型·嵌入式
空圆小生29 分钟前
Vue3 + Spring Boot 全栈实战:从零搭建在线彩票模拟系统
java·spring boot·后端
小马爱打代码32 分钟前
SpringBoot + 分布式锁 + 事务日志:跨服务操作原子性兜底方案
spring boot·分布式·后端
Rust研习社34 分钟前
从 LaunchBadge 到 transact-rs:SQLx 社区迈出可持续治理的第一步
开发语言·后端·rust
真实的菜36 分钟前
Spring Boot 2.2.x 优雅停机实践指南
spring boot·后端
宸津-代码粉碎机1 小时前
Spring AI企业级RAG进阶|文档智能分片调优、ES深度整合、接口限流熔断监控生产实战
java·开发语言·人工智能·后端·spring·elasticsearch·oracle