SegmentFault 最新的文章 |
- 声临其境,轻松几步教你把音频变成3D环绕音
- 【建议收藏】11+实战技巧,让你轻松从Vue过渡到React
- TikTok 被指控“违反” OBS 相关 GPL 协议?或因新 Live Studio 直播应用程序而陷入争议
- 2021 中国开源先锋 33 人评选启动,快来推荐你心尖上的开源先锋吧!
- MVCC 水略深,但是弄懂了真的好爽!
- 倍受关注的 Cilium Service Mesh 到底怎么玩? - 上手实践
- go-zero分布式事务实践
- 面试官:要不我们聊一下“心跳”的设计?
Posted: 14 Dec 2021 12:23 AM PST 在音乐创作、音视频剪辑和游戏等领域中,给用户带来沉浸式音频体验越来越重要。开发者如何在应用内打造3D环绕声效?华为音频编辑服务6.2.0版本此次带来了空间动态渲染功能,可以将人声、乐器等音频元素渲染到指定的三维空间方位,支持静态和动态渲染两种模式,进一步提升应用中的音效体验。开发者可以点击查看以下Demo演示,了解集成效果并上手实验功能特性。 开发实战1. 开发准备开发者提前准备音乐素材,MP3格式最佳。其他音频格式请参考"2.4"步骤转换,视频格式请参考"2.5"步骤进行音频提取。 1.1项目级build.gradle里配置Maven仓地址:
1.2 文件头增加配置:
1.3 应用级build.gradle里配置SDK依赖:
1.4在AndroidManifest.xml文件中申请如下权限:
2.代码开发2.1创建应用自定义的activity界面,用于选择音频,并将该音频文件路径返回给音频编辑SDK:
2.2在UI界面导入音频时,SDK会发送一个action值为com.huawei.hms.audioeditor.chooseaudio的intent以跳转到该activity。因此,该activity"AndroidManifest.xml"中的注册形式如下:
2.3启动音频编辑页面,点击"添加音频",SDK会主动调用"2.1"步骤中定义的activity。添加好音频,就可以进行音频编辑、特效添加等操作,完成后导出编辑音频。
2.4.如果音频素材不是MP3格式,此步骤可以完成音频格式转换: 调用transformAudioUseDefaultPath接口进行音频格式转换,转换后的音频文件导出到默认路径。
调用transformAudio接口进行音频格式转换,转换后的音频文件导出到目标路径。
2.5如果素材是视频格式,可以调用extractAudio接口进行音频提取,从视频中提取音频文件再导出到指定目录:
2.6调用getInstruments和startSeparationTasks接口进行伴奏提取。
2.7调用applyAudioFile接口进行空间方位渲染。
完成以上步骤,就可以得到对应的空间动态渲染效果,在应用内轻松实现2D转3D音效啦!这项功能还可以应用到企业会议以及运动康复领域,比如在展会上进行产品沉浸式展示、作为视障人群的方向感线索,为日常生活提供便利等。开发者们可以根据自己应用的实际需求选择使用,如需了解更多详情,请参考: 了解更多详情>> 访问华为开发者联盟官网 关注我们,第一时间了解 HMS Core 最新技术资讯~ | ||||||||||||||||||||
【建议收藏】11+实战技巧,让你轻松从Vue过渡到React Posted: 21 Dec 2021 08:56 AM PST 前言在这个
1. v-if我们先从最常见的显示隐藏开始,Vue中处理一个元素的显示隐藏一般会用 Vue
React
预览2. v-show同上,这次我们通过 注意: 这里为啥显示的时候不设置为 Vue
React
预览3. v-for一般情况下,渲染一个列表在 Vue
React在
预览4. computed当某个变量需要依赖其他变量求值时,使用计算属性会非常方便,并且 我们来看一个简单的加法例子: Vue
React
预览5. watch有时候我们需要监听数据变化然后执行异步行为或者开销较大的操作时,在Vue中可以使用 我们来模拟一个这样的场景并且通过 Vue
React
预览6. style有时候难免要给元素动态添加样式 在使用上基本大同小异: 相同点: CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名 不同点:
Vue
React
预览7. class如何动态地给元素添加class? Vue中我自己比较喜欢用数组的语法(当然还有对象的写法),React中也可以使用一些第三方包如classnames起到更加便捷添加class的效果。 下面我们看下不借助任何库,如何实现按钮选中的效果 Vue
React
预览8.provide/injectVue和React中对于全局状态的管理都有各自好的解决方案,比如Vue中的 Vue中可以使用provide/inject React中则可以使用Context
Vue
app.vue
provide.vue
React
context/index.js
app.js
provide.js
预览9. slot(默认插槽)插槽是 假设我们要实现一个简单的 Vuedialog
slot
React要在React中同样实现上面的功能应该怎么办呢? Dialog
slot
预览10. name slot(具名插槽)当组件内部有多个动态内容需要外部来填充的时候,一个默认插槽已经不够用了,我们需要给插槽取个名字,这样外部才可以"按部就班"到指定位置。 我们来丰富一下 VueVue中通过 Dialog改造
nameSlot
React前面通过 Dialog改造
nameSlot
预览可以看到具名插槽, 11. scope slot(作用域插槽)有了 假设: VueDialog
scopeSlot
React还是那句话,React中万物皆可传,类似实现具名插槽中我们直接传递DOM,同样我们也可以传递函数,将 scopeSlot源代码点这里
scopeSlot
预览 | ||||||||||||||||||||
TikTok 被指控“违反” OBS 相关 GPL 协议?或因新 Live Studio 直播应用程序而陷入争议 Posted: 20 Dec 2021 09:09 PM PST 据 THEVERGE 报道,上周晚些时候 TikTok 发布的新款 Live Studio Windows 应用程序代码因采用了开源流媒体项目(Open Broadcaster Software project) OBS Studio 的应用程序及其他开源项目,"未遵守各自的开源许可条款",而在推特上受到指控。 12 月 16 日,就有不少用户在推特上发布了疑似 Live Studio 代码的截图,也让该事件在推特上引发了不小的关注。 最初拍摄屏幕截图的程序员声称,该应用程序"是非法的 OBS 分支",并表示" TikTok 使用了OBS,然后在其顶部安装了自己的用户界面"。 还有一位推特用户指出,"如果 TikTok 确实使用了 OBS 的代码,则平台需要根据 GNU 通用公共许可证(GPL)第 2 版公开源代码。如果 TikTok 未能做到这一点,OBS 可能会对平台采取法律行动"。 12 月 17 日,OBS 的相关业务开发者 Ben Torell 在得到同意的情况下回复了这条推特。考虑到 OBS 对于和 TikTok 的合作持开放态度, Torell 在推文中表示:"我们承诺真诚地处理违反 GPL 的行为,对于 TikTok/ByteDance 来说,只要他们遵守许可证,我们很乐意与他们建立友好的工作关系。" 同时,Torell 证实了其团队在通过协议联系时发现了这些违规行为的"明确证据"。Torell 称已与 TikTok 方面取得联系,但尚未得到回应。 据悉,TikTok 上周低调发布的 Live Studio 应用程序,是一款基于 Windows 的应用程序,旨在帮助用户制作高质量的直播流,该程序支持直播整合视频游戏流、图像和文本叠加等功能。 据报道,几天前 TikTok 已经 "悄悄"与一小群用户一起测试了 Live Studio。目前状态下,Live Studio 似乎是一款"原装裸机"的流媒体软件,可支持用户通过相机、手机、游戏以及节目进行现场直播。据 TikTok 公司透漏,该应用程序目前仅面向几个市场的数千名用户可用。 OBS Studio 是一款受欢迎的应用程序,被许多直播流媒体使用。包括 Reddit 在内的许多公司也使用 OBS Studio 代码构建自己的直播软件。根据 OBS Studio 发布的 GPL 条款,这些公司也必须在同一许可证下公开任何修改的源代码。 上个月,OBS 刚刚卷入了一场与 Streamlabs 的"争端",这场争端也同样在 Twitter 上展开。事件起因是:OBS 承认并未允许 Streamlabs 以自己的名义使用 OBS,但 Streamlabs 还是使用了它。随后,由于 Pokimane 等流行的 streamers 公司都纷纷"威胁"停止使用和推广 Streamlabs,Streamlabs 最终才同意从其名称中删除了 "OBS"。 众所周知,如果项目或代码是开源的,如果操作得当,这类违规事件通常不会造成问题。同样 OBS 也是是开源的,但在这里, TikTok 或涉嫌未能遵守 OBS 的许可要求,因此才引起了不小关注。 当然,对于此次事件,Torell 也在推特上表示,OBS 软件正试图避免法律冲突。所以该事件的后续,我们也将持续关注,同时也期待两家公司后续能传来友好合作的相关消息。 | ||||||||||||||||||||
2021 中国开源先锋 33 人评选启动,快来推荐你心尖上的开源先锋吧! Posted: 21 Dec 2021 12:05 AM PST 前言2021 年 3 月 13 日,《中华人民共和国国民经济和社会发展第十四个五年规划和 2035 年远景目标纲要》(以下简称"目标纲要",点击此处阅读全文)正式发布。"开源" 被首次写入国家 "五年规划",云计算、大数据、智能制造等关键词被多次提及。 "十四五" 是我国开启全面建设社会主义现代化国家新征程的第一个五年,全球新一轮科技革命和产业变革深入发展,软件和信息技术服务业迎来新的发展机遇。 SegmentFault 思否作为中国领先的新一代开发者社区,依托数百万开发者用户数据分析,及各科技企业厂商和个人在国内技术领域的行为、影响力指标,展开了 2021 年"中国技术先锋"年度评选。 在去年榜单的基础上,基于 SegmentFault 思否团队在全年对于 "开源" 领域的重点关注和观察,我们继续联合专业的开源联盟开源社共同推出 2021 年 "中国开源先锋 33 人之心尖上的开源人物" 年度评选。 自主申报将于即日起正式开始,诚邀各位开源同行者关注并于截止日期前推荐或自荐。 2021 中国开源先锋 33 人年度评选启动所谓先锋,我们理解不仅限于开发者,贡献代码的开发者、开源项目发起人、开源布道师、开源治理的先锋人物、关注开源的投资人等对推动开源生态发展有帮助的人都是榜单的目标对象。开源项目、开源治理、开源布道、开源商业、开源教育等都是我们重点关注的领域。
这不是一个千篇一律的人物排行,而是 ——
榜单发布时除发布名单,每位人物还都会配有推介理由,它既是对于 2021 年度的总结也是由 SegmentFault 思否和开源社为你联合推介的 "年度最值得关注的开源人物"。 这不是一份面向所有人的榜单,而是特别面向关注开源领域朋友们的信息参考。而本次评选之所以聚焦于 "人" 而非项目,是因为开源项目/开源商业都只是开源生态中的一环,而 "人" 才是万事万物的本源和基础。(这也是为什么 SegmentFault 2015 年起的第一个榜单就是基于人的 "TopWriter" 的评选。) 我们不依赖营销手段传播榜单,而是通过价值驱动。或许不一定榜单的每个领域都对你有所启发,但我们相信这份榜单中总有几位你不曾重点关注却能为你创造价值、带来启发的人,这便是这份榜单最大的价值。 我们期待有更多可能并不那么 "广为人知" 的开源人物也可以通过这份 "米其林推荐" 被更多人认识。除了开源项目、开源商业以外,开源教育、开源治理、开源知识产权等相对冷门的领域也有所发声。 评选流程 & 参选方式
自主申报即日起正式启动,欢迎点击 "报名链接",填写申报表,推荐或自荐。 评选咨询:pr@sifou.com 媒体合作:bd@sifou.com 相关阅读 | ||||||||||||||||||||
Posted: 20 Dec 2021 10:50 PM PST @[toc] 要搞懂 MVCC,最好是要先懂 InnoDB 中事务的隔离级别,不然单纯看概念很难弄明白 MVCC。 1. 隔离级别1.1 理论MySQL 中事务的隔离级别一共分为四种,分别如下:
四种不同的隔离级别含义分别如下:
如果隔离级别为序列化,则用户之间通过一个接一个顺序地执行当前的事务,这种隔离级别提供了事务之间最大限度的隔离。
在可重复读在这一隔离级别上,事务不会被看成是一个序列。不过,当前正在执行事务的变化仍然不能被外部看到,也就是说,如果用户在另外一个事务中执行同条 SELECT 语句数次,结果总是相同的。(因为正在执行的事务所产生的数据变化不能被外部看到)。
READ COMMITTED 隔离级别的安全性比 REPEATABLE READ 隔离级别的安全性要差。处于 READ COMMITTED 级别的事务可以看到其他事务对数据的修改。也就是说,在事务处理期间,如果其他事务修改了相应的表,那么同一个事务的多个 SELECT 语句可能返回不同的结果。
READ UNCOMMITTED 提供了事务之间最小限度的隔离。除了容易产生虚幻的读操作和不能重复的读操作外,处于这个隔离级的事务可以读到其他事务还没有提交的数据,如果这个事务使用其他事务不提交的变化作为计算的基础,然后那些未提交的变化被它们的父事务撤销,这就导致了大量的数据变化。 在 MySQL 数据库种,默认的事务隔离级别是 REPEATABLE READ 1.2 SQL 实践接下来通过几条简单的 SQL 向读者验证上面的理论。 1.2.1 查看隔离级别通过如下 SQL 可以查看数据库实例默认的全局隔离级别和当前 session 的隔离级别: MySQL8 之前使用如下命令查看 MySQL 隔离级别:
查询结果如图: 可以看到,默认的隔离级别为 REPEATABLE-READ,全局隔离级别和当前会话隔离级别皆是如此。 MySQL8 开始,通过如下命令查看 MySQL 默认隔离级别:
就是关键字变了,其他都一样。 通过如下命令可以修改隔离级别(建议开发者在修改时修改当前 session 隔离级别即可,不用修改全局的隔离级别):
上面这条 SQL 表示将当前 session 的数据库隔离级别设置为 READ UNCOMMITTED,设置成功后,再次查询隔离级别,发现当前 session 的隔离级别已经变了,如图1-2: 注意,如果只是修改了当前 session 的隔离级别,则换一个 session 之后,隔离级别又会恢复到默认的隔离级别,所以我们测试时,修改当前 session 的隔离级别即可。 1.2.2 READ UNCOMMITTED1.2.2.1 准备测试数据READ UNCOMMITTED 是最低隔离级别,这种隔离级别中存在脏读、不可重复读以及幻象读问题,所以这里我们先来看这个隔离级别,借此大家可以搞懂这三个问题到底是怎么回事。 下面分别予以介绍。 首先创建一个简单的表,预设两条数据,如下: 表的数据很简单,有 javaboy 和 itboyhub 两个用户,两个人的账户各有 1000 人民币。现在模拟这两个用户之间的一个转账操作。 注意,如果读者使用的是 Navicat 的话,不同的查询窗口就对应了不同的 session,如果读者使用了 SQLyog 的话,不同查询窗口对应同一个 session,因此如果使用 SQLyog,需要读者再开启一个新的连接,在新的连接中进行查询操作。 1.2.2.2 脏读一个事务读到另外一个事务还没有提交的数据,称之为脏读。具体操作如下:
可以看到,A 窗口中的事务,虽然还未提交,但是 B 窗口中已经可以查询到数据的相关变化了。 这就是脏读问题。 1.2.2.3 不可重复读不可重复读是指一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。具体操作步骤如下(操作之前先将两个账户的钱都恢复为1000):
前两条 SQL 执行结果如下:
4.再次回到 B 窗口,执行 B 窗口的第二条 SQL 查看 javaboy 的账户,结果如下: javaboy 的账户已经发生了变化,即前后两次查看 javaboy 账户,结果不一致,这就是不可重复读。 和脏读的区别在于,脏读是看到了其他事务未提交的数据,而不可重复读是看到了其他事务已经提交的数据(由于当前 SQL 也是在事务中,因此有可能并不想看到其他事务已经提交的数据)。 1.2.2.4 幻象读幻象读和不可重复读非常像,看名字就是产生幻觉了。 我举一个简单例子。 在 A 窗口中输入如下 SQL:
然后在 B 窗口输入如下 SQL:
我们执行步骤如下:
这就是幻读。 看了上面的案例,大家应该明白了脏读、不可重复读以及幻读各自是什么含义了。 1.2.3 READ COMMITTED和 READ UNCOMMITTED 相比,READ COMMITTED 主要解决了脏读的问题,对于不可重复读和幻象读则未解决。 将事务的隔离级别改为 上面那个案例不适用于幻读的测试,我们换一个幻读的测试案例。 还是两个窗口 A 和 B,将 B 窗口的隔离级别改为 然后在 A 窗口输入如下测试 SQL:
在 B 窗口输入如下测试 SQL:
测试方式如下:
1.2.4 REPEATABLE READ和 READ COMMITTED 相比,REPEATABLE READ 进一步解决了不可重复读的问题,但是幻象读则未解决。 REPEATABLE READ 中关于幻读的测试和上一小节基本一致,不同的是第二步中执行完插入 SQL 后记得提交事务。 由于 REPEATABLE READ 已经解决了不可重复读,因此第二步即使提交了事务,第三步也查不到已经提交的数据,第四步继续插入就会出错。 注意,REPEATABLE READ 也是 InnoDB 引擎的默认数据库事务隔离级别 1.2.5 SERIALIZABLESERIALIZABLE 提供了事务之间最大限度的隔离,在这种隔离级别中,事务一个接一个顺序的执行,不会发生脏读、不可重复读以及幻象读问题,最安全。 如果设置当前事务隔离级别为 SERIALIZABLE,那么此时开启其他事务时,就会阻塞,必须等当前事务提交了,其他事务才能开启成功,因此前面的脏读、不可重复读以及幻象读问题这里都不会发生。 1.3 总结总的来说,隔离级别和脏读、不可重复读以及幻象读的对应关系如下:
性能关系如图: 松哥前不久也录过一个隔离级别的视频,大家可以参考下: 2. 快照读与当前读接下来我们还需要搞明白一个问题:快照读与当前读。 2.1 快照读快照读(SnapShot Read)是一种一致性不加锁的读,是 InnoDB 存储引擎并发如此之高的核心原因之一。 在可重复读的隔离级别下,事务启动的时候,就会针对当前库拍一个照片(快照),快照读读取到的数据要么就是拍照时的数据,要么就是当前事务自身插入/修改过的数据。 我们日常所用的不加锁的查询,包括本文第一小节中涉及到的所有查询,都属于快照读,这个我就不演示了。 2.2 当前读与快照读相对应的就是当前读,当前读就是读取最新数据,而不是历史版本的数据,换言之,在可重复读隔离级别下,如果使用了当前读,也可以读到别的事务已提交的数据。 松哥举个例子: MySQL 事务开启两个会话 A 和 B。 首先在 A 会话中开启事务并查询 id 为 1 的记录: 接下来我们在 B 会话中对 id 为 1 的数据进行修改,如下: 注意 B 会话不要开启事务或者开启了及时提交事务,否则 update 语句占用一把排他锁会导致一会在 A 会话中用锁时发生阻塞。 接下来,回到 A 会话中继续做查询操作,如下: 可以看到,A 会话中第一个查询是快照读,读取到的是当前事务开启时的数据状态,后面两个查询则是当前读,读取到了当前最新的数据(B 会话中修改后的数据)。 3. undo log我们再来稍微了解一下 undo log,这也有助于我们理解后面的 MVCC,这里我们简单介绍一下。 我们知道数据库事务有回滚的能力,既然能够回滚,那么就必须要在数据改变之前先把旧的数据记录下来,作为将来回滚的依据,那么这个记录就是 undo log。 当我们要添加一条记录的时候,就把添加的数据 id 记录到 undo log 中,将来回滚的时候就据此把数据删除;当我们要删除或者修改数据的时候,就把原数据记录到 undo log 中,将来据此恢复数据。查询操作因为不涉及回滚操作,所以就不需要记录到 undo log 中。 4. 行格式接下来我们再来看一看行格式,这也有助于我们理解 MVCC。 行格式就是 InnoDB 在保存每一行的数据的时候,究竟是以什么样的格式来保存这行数据的。 数据库中的行格式有好几种,例如 COMPACT、REDUNDANT、DYNAMIC、COMPRESSED 等,不过无论是哪种行格式,都绕不开下面几个隐藏的数据列: 上图中的列 1、列 2、列 3 一直到列 N,就是我们数据库中表的列,保存着我们正常的数据,除了这些保存数据的列之外,还有三列额外加进来的数据,这也是我们这里要重点关注的
好啦,这是关于数据行格式的一些内容。 5. MVCC有了前面小节的预备知识,接下来我们就来正式看一看 MVCC。 MVCC,英文全称是 Multi-Version Concurrency Control,中文译作多版本并发控制。 MVCC 的核心思路就是保存数据行的历史版本,通过对数据行的多个版本进行管理来实现数据库的并发控制。 简单来说,我们平时看到的一条一条的记录,在数据库中保存的时候,可能不仅仅只有一条记录,而是有多个历史版本。 如下图: 这张图理解到位了,我想大家的 MVCC 也就理解的查不多了。 接下来我结合不同的隔离级别来和大家说这张图。 5.1 REPEATABLE READ首先,当我们通过 INSERT\DELETE\UPDATE 去操作一行数据的时候,就会产生一个事务 id,这个事务 id 也会同时保存在行记录中(DB_TRX_ID),也就是说,当前数据行是哪个事务修改后得到的,是有记录的。 INSERT\DELETE\UPDATE 操作都会产生对应的 undo log 日志,每一行记录都有一个 当我们开启一个事务的时候,首先会向 InnoDB 的事务系统申请一个事务 id,这个 id 是一个严格递增的数字,在当前事务开启的一瞬间系统会创建一个数组,数组中保存了目前所有的活跃事务 id,所谓的活跃事务就是指已开启但是还没有提交的事务。 这个数组中的最小值好理解,有的小伙伴可能会误以为数组中的最大值就是的当前事务的 id,其实这个不一定,也有可能更大。因为从申请到 trx_id 到创建数组之间也是需要时间的,这期间可能有其他会话也申请到了 trx_id。 当当前事务想要去查看某一行数据的时候,会先去查看该行数据的
前三种情况应该很好理解,主要是后面两种,松哥举一个简单例子。 比如我们有 A、B、C、D 四个会话,首先 A、B、C 分别开启一个事务,事务 ID 是 3、4、5,然后 C 会话提交了事务,A、B 未提交。接下来 D 会话也开启了一个事务,事务 ID 是 6,那么当 D 会话开启事务的时候,数组中的值就是 [3,4,6]。现在假设有一行数据的 另外还有一个需要注意的地方,就是如果当前事务中涉及到数据的更新操作,那么更新操作是在当前读的基础上更新的,而不是快照读的基础上更新的,如果是后者则有可能导致数据丢失。 我举一个例子,假设有如下表: 现在有两个会话 A 和 B,首先在 A 中开启事务: 然后在会话 B 中做一次修改操作(不用显式开启事务,更新 SQL 内部会开启事务,更新完成后事务会自动提交): 接下来回到会话 A 中,查询该条记录发现值没变,符合预期(目前隔离级别是可重复读),然后在 A 中做一次修改操作,修改完成后再去查询,如下图: 可以看到,更新其实是在 100 的基础上更新的,这个也好理解,要是在 99 的基础上更新,那么就会丢失掉 100 的那次更新,显然是不对的。 其实 MySQL 中的 update 就是先读再更新,读的时候默认就是当前读,即会加锁。所以在上面的案例中,如果 B 会话中显式的开启了事务并且没有没有提交,那么 A 会话中的 update 语句就会被阻塞。 这就是 MVCC,一行记录存在多个版本。实现了读写并发控制,读写互不阻塞;同时 MVCC 中采用了乐观锁,读数据不加锁,写数据只锁行,降低了死锁的概率;并且还能据此实现快照读。 5.2 READ COMMITTEDREAD COMMITTED 和 REPEATABLE READ 类似,区别主要是后者在每次事务开始的时候创建一致性视图(创建数组列出活跃事务 id),而前者则每一个语句执行前都会重新算出一个新的视图。 所以 READ COMMITTED 这种隔离级别会看到别的会话已经提交的数据(即使别的会话比当前会话开启的晚)。 6. 小结MVCC 在一定程度上实现了读写并发,不过它只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下有效。 而 READ UNCOMMITTED 总是会读取最新的数据行,SERIALIZABLE 则会对所有读取的行都加锁,这两个都和 MVCC 不兼容。 好啦,不知道小伙伴们看明白没有,有问题欢迎留言讨论。 | ||||||||||||||||||||
倍受关注的 Cilium Service Mesh 到底怎么玩? - 上手实践 Posted: 20 Dec 2021 06:54 PM PST 大家好,我是张晋涛。 Cilium 是一个基于 eBPF 技术,用于为容器工作负载间提供安全且具备可观测性的网络连接的开源软件。 如果你对 Cilium 还不太了解,可以参考我之前的两篇文章: 最近 Cilium v1.11.0 正式发布了,增加 Open Telemetry 的支持以及其他一些增强特性。同时,也宣布了 Cilium Service Mesh 的计划。当前 Cilium Service Mesh 正处于测试阶段,预期在 2022 年会合并到 Cilium v1.12 版本中。 Cilium Service Mesh 也带来了一个全新的模式。 Cilium 直接通过 eBPF 技术实现的 Service Mesh 相比我们常规的 Istio/Linkerd 等方案,最显著的特点就是将 Sidecar proxy 模型替换成了 Kernel 模型, 如下图: 不再需要每个应用程序旁边都放置一个 Sidecar 了,直接在每台 Node 上提供支持。 我在几个月前就已经知道了这个消息并且进行了一些讨论,最近随着 isovalent 的一篇文章 How eBPF will solve Service Mesh - Goodbye Sidecars ,Cilium Service Mesh 也成为了大家关注的焦点。 本篇我带你实际体验下 Cilium Service Mesh。 安装部署这里我使用 KIND 作为测试环境,我的内核版本是 5.15.8 。 准备 KIND 集群关于 KIND 命令行工具的安装这里就不再赘述了,感兴趣的小伙伴可以参考我之前的文章 《使用KIND搭建自己的本地 Kubernetes 测试环境》。 以下是我创建集群使用的配置文件:
创建集群:
安装 Cilium CLI这里我们使用 Cilium CLI 工具进行 Cilium 的部署。
加载镜像在部署 Cilium 的过程中需要一些镜像,我们可以提前下载后加载到 KIND 的 Node 节点中。如果你的网络比较顺畅, 那这一步可以跳过。
部署 cilium接下来我们直接使用 Cilium CLI 完成部署。注意这里的参数。
查看状态在安装成功后, 可以通过
启用 HubbleHubble 主要是用来提供可观测能力的。在启用它之前,需要先加载一个镜像,如果网络畅通可以跳过。
然后使用 Cilium CLI 开启 Hubble :
测试 7 层 Ingress 流量管理安装LB这里我们可以给 KIND 集群中安装 MetaLB ,以便于我们可以使用 LoadBalancer 类型的 svc 资源(Cilium 会默认创建一个 LoadBalancer 类型的 svc)。如果不安装 MetaLB ,那也可以使用 NodePort 的方式来进行替代。 具体过程就不一一介绍了,直接按下述操作步骤执行即可。
加载镜像这里我们使用
部署测试服务本文中的所有配置文件均可在 https://github.com/tao1234566... 代码仓库中获取。 我们使用如下配置进行测试服务的部署:
新建如下的 Ingress 资源文件:
创建 Ingress 资源,然后可以看到产生了一个新的 LoadBalancer 类型的 svc 。
测试使用 curl 命令进行测试访问,发现可以按照 Ingress 资源中的配置得到正确的响应。查看响应头,我们会发现这里的代理实际上还是使用的 Envoy 来完成的。
测试 CiliumEnvoyConfig在使用上述方式部署 CIlium 后, 它其实还安装了一些 CRD 资源。其中有一个是
部署测试服务可以先进行 Hubble 的 port-forward
默认会监听到 4245 端口上,如果不提前执行此操作就会出现下述内容
如果已经开启 Hubble 的 port-forward ,正常情况下会得到如下输出:
我们也可以同时打开UI看看:
效果图如下: 这个操作实际上会进行如下部署:
我们也可以看看它的 label:
测试这里我们在主机上进行操作下, 先拿到 client2 的 Pod 名称,然后通过 Hubble 命令观察所有访问此 Pod 的流量。
以上输出是由于我们执行了下面的操作:
日志中基本上都是 测试使用 proxy需要先安装 networkpolicy , 我们可以直接从 Cilium CLI 的仓库中拿到。
然后重复上面的请求:
执行另一个请求:
也可以看到如下输出,其中有
其实看请求头更加方便:
之前都是如下:
请求一个不存在的地址:以前请求响应是 404 ,现在是 403 ,并得到如下内容
日志中也都是
我们使用如下内容作为 Envoy 的配置文件,其中包含 rewrite 策略。
测试请求时,发现可以正确的得到响应了。
并且请求
同时:请求
多次请求看日志:
可以看到它真的成功的进行了负载均衡。 总结本文我带你部署了 Cilium Service Mesh,并通过两个示例,带你体验了 Cilium Service Mesh 的工作情况。 整体而言, 这种方式能带来一定的便利性,但它的服务间流量配置主要依靠于 CiliumEnvoyConfig ,不算太方便。 一起来期待它后续的演进! 欢迎订阅我的文章公众号【MoeLove】 | ||||||||||||||||||||
Posted: 19 Dec 2021 04:47 PM PST 背景随着业务的快速发展、业务复杂度越来越高,微服务作为最佳解决方案之一,它解耦服务,降低复杂度,增加可维护性的同时,也带来一部分新问题。 当我们需要跨服务保证数据一致性时,原先的数据库事务力不从心,无法将跨库、跨服务的多个操作放在一个事务中。这样的应用场景非常多,我们可以列举出很多:
面对这些本地事务无法解决的场景,我们需要分布式事务的解决方案,保证跨服务、跨数据库更新数据的一致性。 go-zero与dtm强强联合,推出了在go-zero中无缝接入dtm的极简方案,让分布式事务的使用从未如此简单。 运行一个例子我们来看一个可运行的例子,然后再看如何自己开发完成一个完整的分布式事务 下面以etcd作为注册服务中心,可以按照如下步骤运行一个go-zero的示例:
当您在trans的日志中看到
那就是事务正常完成了 开发接入
整个开发接入的过程很少,前面的注释已经很清晰,就不再赘述了 注意事项在开发接入的过程中,去找*.pb.go的文件中的grpc访问的方法路径时候,一定要找invoke的路径 深入理解动态调用在go-zero使用dtm的分布式事务时,许多的调用是从dtm服务器发起的,例如TCC的Confirm/Cancel,SAGA/MSG的所有调用。 dtm无需知道组成分布式事务的相关业务api的强类型,它是动态的调用这些api。 grpc的调用,可以类比于HTTP的POST,其中:
通过下面这部分代码,dtm就拿到了完整信息,就能够发起完整的调用了
更加完整的例子热心的社区同学Mikael帮忙写了一个内容更加丰富的例子,结合实际应用和子事务屏障,完整的演示了一个线上实际运行的分布式事务,有兴趣的同学可以参考: https://github.com/Mikaelemmmm/gozerodtm 其他方式接入go-zero的微服务还有非etcd的其他方式,我们依次说明他们的接入方式 直连对于直连这种方式,您只需要在上面dtm的etcd配置基础上,将Target设置为空字符串即可。 直连的情况,不需要将dtm注册到注册中心 K8S对于K8S这种方式,您只需要在上面dtm的etcd配置基础上,将Target设置为空字符串即可。 在K8S中,将服务注册到K8S中,是由deployment.yaml完成的,应用内部,不需要进行注册 直播分享预告go-zero的作者和我(dtm的作者)将在12月22日晚21点,在talkgo,联合做一场《go-zero的分布式事务实践》的直播分享,将会带来更多更深入的讨论。欢迎大家届时参加。 直播地址为:https://live.bilibili.com/111... 小结这一次go-zero与dtm的合作,在go生态中,打造了首个原生支持分布式事务的微服务解决方案,意义重大。
欢迎大家使用我们的 | ||||||||||||||||||||
Posted: 13 Dec 2021 08:23 PM PST 你好呀,我是歪歪。 是这样的,我最近又看到了这篇文章《工商银行分布式服务 C10K 场景解决方案 为什么是又呢? 因为这篇文章最开始发布的时候我就看过了,当时就觉得写得挺好的,宇宙行(工商银行)果然是很叼的样子。 但是看过了也就看过了,当时没去细琢磨。 这次看到的时候,刚好是在下班路上,就仔仔细细的又看了一遍。 嗯,常读常新,还是很有收获的。 所以写篇文章,给大家汇报一下我再次阅读之后的一下收获。 文章提要我知道很多同学应该都没有看过这篇文章,所以我先放个链接,[《工商银行分布式服务 C10K 场景解决方案 先给大家提炼一下文章的内容,但是如果你有时间的话,也可以先去细细的读一下这篇文章,感受一下宇宙行的实力。 文章内容大概是这样的。 在宇宙行的架构中,随着业务的发展,在可预见的未来,会出现一个提供方为数千个、甚至上万个消费方提供服务的场景。 在如此高负载量下,若服务端程序设计不够良好,网络服务在处理数以万计的客户端连接时、可能会出现效率低下甚至完全瘫痪的情况,即为 C10K 问题。 C10K 问题就不展开讲了,网上查一下,非常著名的程序相关问题,只不过该问题已经成为历史了。 而宇宙行的 RPC 框架使用的是 Dubbo,所以他们那篇文章就是基于这个问题去展开的: 基于 Dubbo 的分布式服务平台能否应对复杂的 C10K 场景? 为此,他们搭建了大规模连接环境、模拟服务调用进行了一系列探索和验证。 首先他们使用的 Dubbo 版本是 2.5.9。版本确实有点低,但是银行嘛,懂的都懂,架构升级是能不动就不动,稳当运行才是王道。 在这个版本里面,他们搞了一个服务端,服务端的逻辑就是 sleep 100ms,模拟业务调用,部署在一台 8C16G 的服务器上。 对应的消费方配置服务超时时间为 5s,然后把消费方部署在数百台 8C16G 的服务器上(我滴个乖乖,数百台 8C16G 的服务器,这都是白花花的银子啊,有钱真好),以容器化方式部署 7000 个服务消费方。 每个消费方启动后每分钟调用 1 次服务。 然后他们定制了两个测试的场景: .png) 场景 2 先暂时不说,异常是必然的,因为只有一个提供方嘛,重启期间消费方还在发请求,这必然是要凉的。 但是场景 1 按理来说不应该的啊。 你想,消费方配置的超时时间是 5s,而提供方业务逻辑只处理 100ms。再怎么说时间也是够够的了。 需要额外多说一句的是:本文也只聚焦于场景 1。 但是,朋友们,但是啊。 虽然调用方一分钟发一次请求的频率不高,但是架不住调用方有 7000 个啊,这 7000 个调用方,这就是传说中的突发流量,只是这个"突发"是每分钟一次。 所以,偶现超时也是可以理解的,毕竟服务端处理能力有限,有任务在队列里面稍微等等就超时了。 可以写个小例子示意一下,是这样的: 就是搞个线程池,线程数是 200。然后提交 7000 个任务,每个任务耗时 100ms,用 CountDownLatch 模拟了一下并发,在我的 12 核的机器上运行耗时 3.8s 的样子。 也就是说如果在 Dubbo 的场景下,每一个请求再加上一点点网络传输的时间,一点点框架内部的消耗,这一点点时间再乘以 7000,最后被执行的任务理论上来说,是有可能超过 5s 的。 所以偶现超时是可以理解的。 但是,朋友们,又来但是了啊。 我前面都说的是理论上,然而实践才是检验真理的唯一办法。 看一下宇宙行的验证结果: 首先我们可以看到消费方不论是发起请求还是处理响应都是非常迅速的,但是卡壳就卡在服务方从收到请求到处理请求之间。 经过抓包分析,他们得出结论:导致交易超时的原因不在消费方侧,而在提供方侧。 这个结论其实也很好理解,因为压力都在服务提供方这边,所以阻塞也应该是在它这里。 其实到这里我们基本上就可以确认,肯定是 Dubbo 框架里面的某一些操作导致了耗时的增加。 难的就是定位到,到底是什么操作呢? 宇宙行通过一系列操作,经过缜密的分析,得出了一个结论: 心跳密集导致 netty worker 线程忙碌,从而导致交易耗时增长。 也就是结论中提到的这一点: 有了结论,找到了病灶就好办了,对症下药嘛。 因为前面说过,本文只聚焦于场景一,所以我们看一下对于场景一宇宙行给出的解决方案: 全都是围绕着心跳的优化处理,处理完成后的效果如下: 其中效果最显著的操作是"心跳绕过序列化"。 消费方与提供方之间平均处理时差由 27ms 降低至 3m,提升了 89%。 前 99% 的交易耗时从 191ms 下降至 133ms,提升了 30%。 好了,写到这,就差不多是把那篇文章里面我当时看到的一些东西复述了一遍,没啥大营养。 只是我还记得第一次看到这篇文章的时候,我是这样的: 我觉得挺牛逼的,一个小小的心跳,在 C10K 的场景下竟然演变成了一个性能隐患。 我得去研究一下,顺便宇宙行给出的方案中最重要的是"心跳绕过序列化",我还得去研究一下 Dubbo 怎么去实现这个功能,研究明白了这玩意就是我的了啊。 但是... 我忘记当时为啥没去看了,但是没关系,我现在想起来了嘛,马上就开始研究。 心跳如何绕过序列化我是怎么去研究呢? 直接往源码里面冲吗? 是的,就是往源码里面冲。 但是冲之前,我先去 Dubb 的 github 上逛了一圈: https://github.com/apache/dubbo 然后在 Pull request 里面先搜索了一下"Heartbeat",这一搜还搜出不少好东西呢: 我一眼看到这两个 pr 的时候,眼睛都在放光。 好家伙,我本来只是想随便看看,没想到直接定位了我要研究的东西了。 我只需要看看这两个 pr,就知道是怎么实现的"心跳绕过序列化",这直接就让我少走了很多弯路。 首先看这个: https://github.com/apache/dub... 从这段描述中可以知道,我找到对的地方了。而从他的描述中知道"心跳跳过序列化",就是用 null 来代替了序列化的这个过程。 同时这个 pr 里面还说明了自己的改造思路: 接着就带大家看一下这一次提交的代码。 怎么看呢? 可以在 git 上看到他对应这次提交的文件: 到源码里面找到对应地方即可,这也是一个去找源码的方法。 我比较熟悉 Dubbo 框架,不看这个 pr 我也大概知道去哪里找对应的代码。但是如果换成另外一个我不熟悉的框架呢? 从它的 git 入手其实是一个很好的角度。 一个翻阅源码的小技巧,送给你。 如果你不了解 Dubbo 框架也没有关系,我们只是聚焦于"心跳是如何跳过序列化"的这一个点。至于心跳是由谁如何在什么时间发起的,这一节暂时不讲。 接着,我们从这个类下手: org.apache.dubbo.rpc.protocol.dubbo.DubboCodec 从提交记录可以看出主要有两处改动,且两处改动的代码是一模一样的,都位于 decodeBody 这个方法,只是一个在 if 分支,一个在 else 分支: 这个代码是干啥的? 你想一个 RPC 调用,肯定是涉及到报文的 encode(编码) 和 decode(解码) 的,所以这里主要就是对请求和响应报文进行 decode 。 一个心跳,一来一回,一个请求,一个响应,所以有两处改动。 所以我带着大家看请求包这一处的处理就行了: 可以看到代码改造之后,对心跳包进行了一个特殊的判断。 在心跳事件特殊处理里面涉及到两个方法,都是本次提交新增的方法。 第一个方法是这样的: org.apache.dubbo.remoting.transport.CodecSupport#getPayload 就是把 InputStream 流转换成字节数组,然后把这个字节数组作为入参传递到第二个方法中。 第二个方法是这样的: org.apache.dubbo.remoting.transport.CodecSupport#isHeartBeat 从方法名称也知道这是判断请求是不是心跳包。 怎么去判断它是心跳包呢? 首先得看一下发起心跳的地方: org.apache.dubbo.remoting.exchange.support.header.HeartbeatTimerTask#doTask 从发起心跳的地方我们可以知道,它发出去的东西就是 null。 所以在接受包的地方,判断其内容是不是 null,如果是,就说明是心跳包。 通过这简单的两个方法,就完成了心跳跳过序列化这个操作,提升了性能。 而上面两个方法都是在这个类中,所以核心的改动还是在这个类,但是改动点其实也不算多: org.apache.dubbo.remoting.transport.CodecSupport 在这个类里面有两个小细节,可以带大家再看看。 首先是这里: 这个 map 里面缓存的就是不同的序列化的方式对应的 null,代码干的也就是作者这里说的这件事儿: 另外一个细节是看这个类的提交记录: 还有一次优化性的提交,而这一次提交的内容是这样的。 首先定义了一个 ThreadLocal,并使其初始化的时候是 1024 字节: 那么这个 ThreadLocal 是用在哪儿的呢? 在读取 InputStream 的时候,需要开辟一个字节数组,为了避免频繁的创建和销毁这个字节数据,所以搞了一个 ThreadLocal: 有的同学看到这里就要问了:为什么这个 ThreadLocal 没有调用 remove 方法呢,不会内存泄漏嘛? 不会的,朋友们,在 Dubbo 里面执行这个玩意的是 NIO 线程,这个线程是可以复用的,且里面只是放了一个 1024 的字节数组,不会有脏数据,所以不需要移除,直接复用。 正是因为可以复用,所以才提升了性能。 这就是细节,魔鬼都在细节里面。 这一处细节,就是前面提到的另外一个 pr: https://github.com/apache/dub... 看到这里,我们也就知道了宇宙行到底是怎么让心跳跳过序列化操作了,其实也没啥复杂的代码,几十行代码就搞定了。 但是,朋友们,又要但是了。 写到这里的时候,我突然感觉到不太对劲。 因为我之前写过这篇文章,Dubbo 协议那点破事。 在这篇文章里面有这样的一个图: 这是当时在官网上截下来的。 在协议里面,事件标识字段之前只有 0 和 1。 但是现在不一样了,从代码看,是把 1 的范围给扩大了,它不一定代表的是心跳,因为这里面有个 if-else 所以,我就去看了一下现在官网上关于协议的描述。 https://dubbo.apache.org/zh/d... 果然,发生了变化: 并不是说 1 就是心跳包,而是改口为:1 可能是心跳包。 严谨,这就是严谨。 所以开源项目并不是代码改完就改完了,还要考虑到一些周边信息的维护。 心跳的多种设计方案在研究 Dubbo 心跳的时候,我还找到了这样一个 pr。 https://github.com/apache/dub... 标题是这样的: 翻译过来就是使用 IdleStateHandler 代替使用 Timer 发送心跳的建议。 我定睛一看,好机会,这不是 95 后老徐嘛,老熟人了。 看一下老徐是怎么说的,他建议具体是这样的: 几位 Dubbo 大佬,在这个 pr 里面交换了很多想法,我仔细的阅读之后都受益匪浅。 大家也可以点进去看看,我这里给大家汇报一下自己的收获。 首先是几位老哥在心跳实时性上的一顿 battle。 总之,大家知道 Dubbo 的心跳检测是有一定延时的,因为是基于时间轮做的,相当于是定时任务,触发的时效性是不能保证实时触发的。 这玩意就类似于你有一个 60 秒执行一次的定时任务,在第 0 秒的时候任务启动了,在第 1 秒的时候有一个数据准备好了,但是需要等待下一次任务触发的时候才会被处理。因此,你处理数据的最大延迟就应该是 60 秒。 这个大家应该能明白过来。 额外说一句,上面讨论的结果是"目前是 1/4 的 heartbeat 延时",但是我去看了一下最新的 master 分支的源码,怎么感觉是 1/3 的延时呢: 从源码里可以看到,计算时间的时候 HEARTBEAT_CHECK_TICK 参数是 3。所以我理解是 1/3 的延时。 但是不重要,这不重要,反正你知道是有延时的就行了。 而 kexianjun 老哥认为如果基于 netty 的 IdleStateHandler 去做,每次检测超时都重新计算下一次检测的时间,因此相对来说就能比较及时的检查到超时了。 这是在实时性上的一个优化。 而老徐觉得,除了实时性这个考虑外,其实 IdleStateHandler 更是一个针对心跳的优雅的设计。但是呢,由于是基于 Netty 的,所以当通讯框架使用的不是 Netty 的时候,就回天无力了,所以可以保留 Timer 的设计来应对这种情况。 很快,carryxyh 老哥就给出了很有建设性的意见: 由于 Dubbo 是支持多个通讯框架的。 这里说的"多个",其实不提我都忘记了,除了 Netty 之外,它还支持 Girzzly 和 Mina 这两种底层通讯框架,而且还支持自定义。 但是我寻思都 2021 年了,Girzzly 和 Mina 还有人用吗? 从源码中我们也能找到它们的影子: org.apache.dubbo.remoting.transport.AbstractEndpoint Girzzly、Mina 和 Netty 都各有自己的 Server 和 Client。 其中 Netty 有两个版本,是因为 Netty4 步子迈的有点大,难以在之前的版本中进行兼容,所以还不如直接多搞一个实现。 但是不管它怎么变,它都还是叫做 Netty。 好了,说回前面的建设性意见。 如果是采用 IdleStateHandler 的方式做心跳,而其他的通讯框架保持 Timer 的模式,那么势必会出现类似于这样的代码:
这是一个开源框架中不应该出现的东西,因为会增加代码复杂度。 所以,他的建议是最好还是使用相同的方式来进行心跳检测,即都用 Timer 的模式。 正当我觉得这个哥们说的有道理的时候,我看了老徐的回答,我又瞬间觉得他说的也很有道理: 我觉得上面不需要我解释了,大家边读边思考就行了。 接着看看 carryxyh 老哥的观点: 这个时候对立面就出现了。 老徐的角度是,心跳肯定是要有的,只是他觉得不同通讯框架的实现方式可以不必保持一致(现在都是基于 Timer 时间轮的方式),他并不认为 Timer 抽象成一个统一的概念去实现连接保活是一个优雅的设计。 在 Dubbo 里面我们主要用的就是 Netty,而 Netty 的 IdleStateHandler 机制,天生就是拿来做心跳的。 所以,我个人认为,是他首先觉得使用 IdleStateHandler 是一种比较优雅的实现方式,其次才是时效性的提升。 但是 carryxyh 老哥是觉得 Timer 抽象的这个定时器,是非常好的设计,因为它的存在,我们才可以不关心底层是netty还是mina,而只需要关心具体实现。 而对于 IdleStateHandler 的方案,他还是认为在时效性上有优势。但是我个人认为,他的想法是如果真的有优势的话,我们可以参考其实现方式,给其他通讯框架也赋能一个 "Idle" 的功能,这样就能实现大统一。 看到这里,我觉得这两个老哥 battle 的点是这样的。 首先前提是都围绕着"心跳"这个功能。 一个认为当使用 Netty 的时候"心跳"有更好的实现方案,且 Netty 是 Dubbo 主要的通讯框架,所以应该可以只改一下 Netty 的实现。 一个认为"心跳"的实现方案应该统一,如果 Netty 的 IdleStateHandler 方案是个好方案,我们应该把这个方案拿过来。 我觉得都有道理,一时间竟然不知道给谁投票。 但是最终让我选择投老徐一票的,是看了他写的这篇文章:《一种心跳,两种设计》。 这篇文章里面他详细的写了 Dubbo 心跳的演变过程,其中也涉及到部分的源码。 最终他给出了这样的一个图, 心跳设计方案对比: 然后,是这段话: .png) 老徐是在阿里搞中间件的,原来搞中间件的人每天想的是这些事情。 有点意思。 看看代码带大家看一下代码,但是不会做详细分析,相当于是指个路,如果想要深入了解的话,自己翻源码去。 首先是这里: org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeClient 可以看到在 HeaderExchangeClient 的构造方法里面调用了 startHeartBeatTask 方法来开启心跳。 同时这里面有个 HashedWheelTimer,这玩意我熟啊,时间轮嘛,之前分析过的。 然后我们把目光放在这个方法 startHeartBeatTask: 这里面就是构建心跳任务,然后扔到时间轮里面去跑,没啥复杂的逻辑。 这一个实现,就是 Dubbo 对于心跳的默认处理。 但是需要注意的是,整个方法被 if 判断包裹了起来,这个判断可是大有来头,看名字叫做 canHandleIdle,即是否可以处理 idle 操作,默认是 false: 所以,前面的 if 判断的结果是 true。 那么什么情况下 canHandleIdle 是 true 呢? 在使用 Netty4 的时候是 true。 也就是 Netty4 不走默认的这套心跳实现。 那么它是怎么实现的呢? 由于服务端和客户端的思路是一样的,所以我们看一下客户端的代码就行。 关注一下它的 doOpen 方法: org.apache.dubbo.remoting.transport.netty4.NettyClient#doOpen 在 pipeline 里面加入了我们前面说到的 IdleStateHandler 事件,这个事件就是如果 heartbeatInterval 毫秒内没有读写事件,那么就会触发一个方法,相当于是一个回调。 heartbeatInterval 默认是 6000,即 60s。 然后加入了 nettyClientHandler,它是干什么呢? 看一眼它的这个方法: org.apache.dubbo.remoting.transport.netty4.NettyClientHandler#userEventTriggered 这个方法里面在发送心跳事件。 也就是说你这样写,含义是在 60s 内,客户端没有发生读写时间,那么 Netty 会帮我们触发 userEventTriggered 方法,在这个方法里面,我们可以发送一次心跳,去看看服务端是否正常。 从目前的代码来看, Dubbo 最终是采用的老徐的建议,但是默认实现还是没变,只是在 Netty4 里面采用了 IdleStateHandler 机制。 这样的话,其实我就觉得更奇怪了。 同样是 Netty,一个采用的是时间轮,一个采用的 IdleStateHandler。 同时我也很理解,步子不能迈的太大了,容易扯着蛋。 但是,在翻源码的过程中,我发现了一个代码上的小问题。 org.apache.dubbo.remoting.exchange.codec.ExchangeCodec#decode(org.apache.dubbo.remoting.Channel, org.apache.dubbo.remoting.buffer.ChannelBuffer, int, byte[]) 在上面这个方法中,有两行代码是这样的: 你先别管它们是干啥的,我就带你看看它们的逻辑是怎么样的: 可以看到两个方法都执行了这样的逻辑:
如果 finishRespWhenOverPayload 返回的不是 null,没啥说的,返回 return 了,不会执行 checkPayload 方法。 如果 finishRespWhenOverPayload 返回的是 null,则会执行 checkPayload 方法。 这个时候会再次做检查报文大小的操作,这不就重复了吗? 所以,我认为这一行的代码是多余的,可以直接删除。 你明白我意思吧? 又是一个给 Dubbo 贡献源码的机会,送给你,可以冲一波。 最后,再给大家送上几个参考资料。 第一个是可以去了解一下 SOFA-RPC 的心跳机制。 SOFA-PRC 也是阿里开源出来的框架。 在心跳这块的实现就是完完全全的基于 IdleStateHandler 来实现的。 可以去看一下官方提供的这两篇文章: https://www.sofastack.tech/se... 第二个是极客时间《从0开始学微服务》,第 17 讲里面,老师在关于心跳这块的一点分享,提到的一个保护机制,这是我之前没有想到过的: 反正我是觉得,我文章中提到的这一些链接,你都去仔仔细细的看了,那么对于心跳这块的东西,也就掌握的七七八八了,够用了。 好了,就到这吧。 本文已收录至个人博客,欢迎大家来玩。 https://www.whywhy.vip/ |
You are subscribed to email updates from SegmentFault 最新的文章. To stop receiving these emails, you may unsubscribe now. | Email delivery powered by Google |
Google, 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States |
No comments:
Post a Comment