在日常的开发工作中,大家肯定都接触过心跳机制。心跳机制被广泛应用于多种场景,以确保服务的高可用性和稳定性。比如Eureka的服务续约,Redis中的哨兵节点, Kubernetes的存活探针等等。今天我们来探究下心跳机制具体是如何实现的。
本文主要介绍三种实现方式,第一种依赖Redis作为缓存,第二种方式从数据库层面入手,第三种方式使用命令。
首先来看第一种方式。
一、基于Redis缓存
每当客户端通过API方式向服务端发送心跳的时候,服务端针对客户端的唯一标识去进行key的判断。如果key存在,就延长key的过期时间;如果key不存在就新增一个key,并设置过期时间。
以下是代码示例。
public class HeartBeat{
@Autowired
private StringRedisTemplate redisTemplate;
// 该变量可以从环境变量中取
@Value("${keep.alive.time:30}")
private Integer keepAliveTime;
// 方式1
public void sendHeartbeat1(@RequestParam String bizId){
if(redisTemplate.hasKey(bizId)){
// key存在就延长key的过期时间
redisTemplate.expire(bizId, keepAliveTime, TimeUnits.MINUTES);
} else{
// key不存在就添加一个,并设置过期时间。
redisTemplate.opsForValue().set(bizId, bizId, keepAliveTime, TimeUnits.MINUTES)
}
}
}
以下是Redis的Jar包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
最简单的方式就是采用单机部署,然后在.yml或者.properties文件中配置redis服务端的地址和端口,以及password即可。
接下来是第二种方式。
二、基于数据库
每当客户端通过API的方式向服务端发送心跳的时候,服务端更新表中的心跳时间字段。
业务逻辑check节点是否存活时,只需要判断表中心跳的最近一次更新时间距离当前时间大于发送心跳的时间间隔就可以确认客户端是否挂了。
这里假定表名为node_manage, 表名对应的实体类为NodeManage。以下是代码示例。
// 方式2
@AutoWired
private NoteManageMapper noteManageMapper;
public void sendHeartbeat2(@RequestParam String bizId){
// crud使用mybatis-plus提供的BaseMapper接口的方法
QueryWrapper<NoteManage> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(NoteManage::getBizId, bizId);
NoteManage noteManage = nodeManageMapper.selectOne(queryWrapper);
if(ObjectUtils.isEmpty(noteManage)){
// 可以加个log
return;
}
NoteManage updateNoteManage = NoteManage.builder().heartBeatTime(OffsetDateTime.now()).id(noteManage.getId()).builder();
updateWrapper.set("heartbeat_time", OffsetDateTime.now());
int i = nodeManageMapper.updateById(updateNoteManage);
if(i == 0){
// 可以加个log
return;
}
}
@Mapper
public interface NodeManageMapper extends BaseMapper<NoteManage>{
}
以下是mybatis-plus的Jar包
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${version}</version>
</dependency>
三、基于PING命令
以Redis的哨兵机制为例,它使用PING命令作为心跳机制来监控Redis各个节点的状态。
哨兵节点会默认每隔1秒向主节点和从节点发送一次PING命令。一旦节点在规定时间内未作出响应,哨兵节点便会判定该 Redis 节点已掉线。通过这种方式,确保在主节点出现故障时能够自动进行故障转移,从而提高整个系统的可用性。
最后,我们来总结下,这三种方式的优缺点。
- 基于Redis缓存:优点是实现简单,能够快速处理大量的心跳;缺点是高度依赖Redis服务,如果服务节点挂掉,会导致服务短暂的不可用,所有依赖该心跳机制的节点都会受到影响。
- 基于数据库:优点是实现方式简单直接,数据存储相对稳定可靠;缺点是当表的数据量很大时(千万或亿级别),每次只更新一个时间字段会造成资源的浪费。
- 基于PING命令:优点是命令简单直接,不需要复杂的数据存储和处理逻辑;缺点是完全依赖网络通信,如果网络出现问题,可能会导致误判节点状态。
当然,除了这三种方式,还有很多其它的实现心跳机制的方式,欢迎大家在评论区讨论。