网络编程之KeepAlive vs. heart-beat

本文探讨了TCP连接管理中的keepalive与heart-beat两种方法。keepalive适用于长时间无活动连接的清理,而heart-beat则用于实时监控连接状态。文章对比了二者的工作原理与应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这周在上班的路上看了本书《Effective TCP/IP Programming》,以下是一些读书笔记。顺带推荐一下这本书,写的很棒,适用于像我这样经常要写一些有一定质量的网络编程,但又没时间啃那些讲解TCPIP协议大部头书的人。

很多人都知道TCP并不会去主动检测连接的丢失,这意味着,如果双方不产生交互,那么如果网络断了或者有一方机器崩溃,另外一方将永远不知道连接已经不可用了。检测连接是否丢失的方法大致有两种:keepalive和heart-beat。

Keepalive是很多的TCP实现提供的一种机制,它允许连接在空闲的时候双方会发送一些特殊的数据段,并通过响应与否来判断连接是否还存活着(所谓keep~~alive)。我曾经写过一篇关于keepalive的blog ,但后来我也发现,其实keepalive在实际的应用中并不常见。为何如此?这得归结于keepalive设计的初衷。Keepalive适用于清除死亡时间比较长的连接。
比如这样的场景:一个用户创建tcp连接访问了一个web服务器,当用户完成他执行的操作后,很粗暴的直接拨了网线。这种情况下,这个tcp连接已经断开了,但是web服务器并不知道,它会依然守护着这个连接。如果web server设置了keepalive,那么它就能够在用户断开网线的大概几个小时以后,确认这个连接已经中断,然后丢弃此连接,回收资源。
采用keepalive,它会先要求此连接一定时间没有活动(一般是几个小时),然后发出数据段,经过多次尝试后(每次尝试之间也有时间间隔),如果仍没有响应,则判断连接中断。可想而知,整个周期需要很长的时间。
所以,如前面的场景那样,需要一种方法能够清除和回收那些在系统不知情的情况下死去了很久的连接,keepalive是非常好的选择。
但是,在大部分情况下,特别是分布式环境中,我们需要的是一个能够快速或者实时监控连接状态的机制,这里,heart-beat才是更加合适的方案。
Heart-beat(心跳),按我的理解,它的原理和keepalive非常类似,都是发送一个信号给对方,如果多次发送都没有响应的话,则判断连接中断。它们的不同点在于,keepalive是tcp实现中内建的机制,是在创建tcp连接时通过设置参数启动keepalive机制;而heart-beat则需要在tcp之上的应用层实现。一个简单的heart-beat实现一般测试连接是否中断采用的时间间隔都比较短,可以很快的决定连接是否中断。并且,由于是在应用层实现,因为可以自行决定当判断连接中断后应该采取的行为,而keepalive在判断连接失败后只会将连接丢弃。
关于heart-beat,一个非常有趣的问题是,应该在传输真正数据的连接中发送“心跳”信号,还是可以专门创建一个发送“心跳”信号的连接。比如说,A,B两台机器之间通过连接m来传输数据,现在为了能够检测A,B之间的连接状态,我们是应该在连接m中传输“心跳”信号,还是创建新的连接n来专门传输“心跳”呢?我个人认为两者皆可。如果担心的是端到端的连接状态,那么就直接在该条连接中实现“心跳”。但很多时候,关注的是网络状况和两台主机间的连接状态,这种情况下, 创建专门的“心跳”连接也未尝不可。

### Vue3 中 `keep-alive` 和 `router.go(-1)` 的交互关系 在 Vue3 中,`<keep-alive>` 是一个内置组件,用于缓存动态组件实例,从而避免重复渲染已加载过的组件。而 `router.go(-1)` 是 Vue Router 提供的一个方法,用于导航到历史记录中的前一页。 #### `<keep-alive>` 工作原理 当使用 `<keep-alive>` 包裹动态组件时,Vue 会将这些组件的状态保存下来,在切换回该组件时无需重新初始化数据或执行生命周期钩子函数[^1]。这意味着如果某个页面被包裹在 `<keep-alive>` 内部,则其状态会被保留,直到它再次显示出来为止。 #### `router.go(-1)` 行为分析 调用 `router.go(-1)` 实际上相当于浏览器的“返回”按钮操作,即回到浏览历史栈中的上一位置。此过程不会触发新的路由匹配逻辑,而是恢复之前访问过的内容及其当时的状态[^2]。 #### 结合使用的注意事项 - 如果目标页面未被 `<keep-alive>` 缓存起来,则即使通过 `router.go(-1)` 返回也不会保持原有状态;只有那些处于活动缓存列表内的视图才能维持它们离开时的样子。 - 需要注意的是,默认情况下所有的子路由都会受到父级 `<keep-alive>` 影响,因此可能需要设置 include 或 exclude 属性来精确控制哪些组件应该参与缓存机制。 以下是具体实现代码示例: ```javascript // main.js import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; const app = createApp(App); app.use(router); export default { install(app){ app.component('shared-element', SharedElementDirective); // 假设这是自定义指令注册部分 } } // 路由配置文件 router/index.js 示例片段 import { createRouter, createWebHistory } from 'vue-router'; import HomeView from '../views/HomeView.vue'; const routes = [ { path: '/', name: 'home', component: () => import(/* webpackChunkName: "about" */ '../views/HomeView.vue'), meta:{ keepAlive:true }, // 添加元信息标记以便于判断是否启用缓存功能 }, ]; const router = createRouter({ history:createWebHistory(process.env.BASE_URL), routes, }); function beforeEnter(to,from,next){ console.log(`Navigating to ${to.fullPath}`); next(); } for(let route of routes){ if(route.meta && typeof route.meta.keepAlive !== undefined ){ Object.assign(route,{beforeEnter}); } } ``` ```html <!-- 在模板中 --> <template> <div id="app"> <!-- 使用 v-if 控制条件渲染 --> <keep-alive :include="cachedViews"> <router-view/> </keep-alive> <button @click="$router.back()">Go Back</button> <!-- 可替代写法 --> <button @click="handleBackClick">Custom Go Back</button> </div> </template> <script> export default{ methods:{ handleBackClick(){ this.$router.go(-1); } } }; </script> ``` 上述例子展示了如何利用 `<keep-alive>` 来管理特定视图的缓存行为,并且提供了两种方式去实现页面退回的功能——一种是直接绑定 `$router.back()` 方法至 HTML 元素事件处理器之中,另一种则是封装成独立的方法再加以调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值