第十一章 第一次技术分享
一
赵北辰在周一的站会上宣布了一件事:"从这个月开始,我们组每两周做一次技术分享。每个人轮流讲,每次三十到四十五分钟。"
他看了一圈:"第一个,志远。"
王志远的心跳漏了一拍。
"我?"
"对。你上次做的消息通知中心,事件驱动的设计方案很有参考价值。整理一下,下周五给大家讲讲。"
"可是我……我没做过技术分享。"
"所以才让你做。"赵北辰笑了笑。"学一个东西最好的方式,就是把它讲给别人听。讲不清楚,说明你自己还没真正理解。"
王志远回到工位上,手心冒汗。
他不怕写代码,不怕改 bug,不怕凌晨两点的 On-Call。但他怕站在一群人面前说话。
从小到大,他都是那个在课堂上低头不敢举手的人。大学做课程答辩的时候,他紧张得声音发抖,PPT 翻错了两页,最后草草收场。
现在要他在一群技术比他强的同事面前做分享?
他觉得自己要完了。
二
他花了一个晚上列提纲,发现自己虽然做了消息通知中心,但要把它讲清楚,远比做出来难。
做的时候,他只需要自己理解就行。讲的时候,他需要让别人也理解——而且是在四十五分钟内。
他列了一个初步的提纲:
- 背景:为什么要做消息通知中心
- 设计:事件驱动架构
- 实现:代码细节
- 总结
他看着这个提纲,觉得很干。就像一份技术文档的目录——信息是全的,但没有人愿意听。
他想起大学时听过的一些技术讲座。那些让他昏昏欲睡的讲座,都有一个共同特点:从头到尾都在讲"我做了什么",没有讲"为什么这么做"和"踩了什么坑"。
而那些让他印象深刻的讲座,都是从一个问题或者一个故事开始的。
他重新列了提纲:
- 开场:一个真实的问题——"我的第一版设计为什么失败了"
- 问题分析:紧耦合的代价——改一个通知渠道要改五个 Service
- 解决方案:事件驱动架构——发布者和消费者解耦
- 实战演示:现场加一个新的通知渠道,只需要五分钟
- 踩坑记录:我在实现过程中遇到的三个坑
- 总结与思考:什么时候该用事件驱动,什么时候不该用
这个提纲好多了。有故事、有问题、有方案、有演示、有踩坑、有思考。
三
接下来的一周,他每天晚上都在准备。
他用了一个叫 SQ3R 的方法来整理自己的知识——这是他在一本学习方法的书里看到的:
- Survey(浏览):先快速浏览整个主题,建立全局观
- Question(提问):针对每个部分提出问题——"为什么要用事件驱动?""事件驱动有什么缺点?"
- Read(阅读):带着问题深入阅读和研究
- Recite(复述):用自己的话把学到的东西讲出来
- Review(回顾):过一段时间后再回顾,检查是否遗忘
他发现,在准备分享的过程中,自己对事件驱动架构的理解又深了一层。
比如,他之前只知道 Spring 的 ApplicationEventPublisher 是同步的——事件发布后,所有监听器在同一个线程里依次执行。但在准备分享的时候,他深入研究了一下,发现如果监听器里有耗时操作(比如发短信),会阻塞主流程。解决方案是给监听器加 @Async 注解,让它在异步线程里执行。
他还发现了一个自己之前没注意到的问题:如果事件监听器抛出异常,默认情况下异常会传播到事件发布者,可能导致主流程失败。需要在监听器里做好异常处理,或者用 @TransactionalEventListener 来控制事件的执行时机。
这些细节,如果不是为了准备分享,他可能永远不会去深入研究。
他突然理解了赵北辰说的那句话:"学一个东西最好的方式,就是把它讲给别人听。"
这就是费曼学习法——通过"教"来"学"。当你试图把一个概念讲给别人听的时候,你会发现自己理解中的漏洞。那些你以为自己懂了的东西,在讲的时候才发现其实没有真正理解。
四
周四晚上,他在出租屋里对着镜子练了一遍。
效果很差。
他对着镜子说话的时候,声音不自觉地变小,眼神飘忽,手不知道往哪放。说到技术细节的时候还好,一到需要"讲故事"的部分就卡壳——"我的第一版设计为什么失败了"这个开场,他练了五遍都觉得不自然。
他给赵北辰发了一条消息:"赵哥,明天的分享我有点紧张。有什么建议吗?"
赵北辰回复了三条:
- "紧张是正常的。我第一次做技术分享的时候,手抖得连激光笔都拿不稳。"
- "不要背稿。你对这个主题足够熟悉,用自己的话讲就行。PPT 是提词器,不是剧本。"
- "记住一件事:台下的人不是来挑你毛病的,是来学东西的。你只要让他们学到一个新东西,这次分享就是成功的。"
王志远看着第三条消息,心里稍微踏实了一点。
他又练了两遍,这次不对着镜子了,而是对着电脑屏幕上的 PPT,假装台下有人。
还是紧张,但比第一遍好多了。
五
周五下午两点,会议室。
全组十来个人坐在下面。赵北辰、苏薇、陈锐、马德华,还有几个其他小组的同事——赵北辰把分享的消息发到了部门群里,吸引了一些"外援"。
王志远站在投影幕前,手里攥着翻页笔。
"大家好,我是王志远。今天分享的主题是'事件驱动架构在消息通知中心的实践'。"
他的声音有点发抖。他能感觉到自己的心跳在加速。
"在开始之前,我想先讲一个故事——一个关于'我是怎么把代码写成一坨的'的故事。"
台下有人笑了。
他的紧张感减轻了一点。
"两个月前,我接到了一个需求——做一个消息通知中心。我当时觉得不复杂,直接开始写代码。写了两周之后,我的 NotificationService 变成了这样——"
他翻到一页 PPT,上面是一段密密麻麻的 if-else 代码。
"七种通知类型,四种发送渠道,全部塞在一个方法里。每加一个新类型,我就要在这个方法里加一个 if-else。每加一个新渠道,我就要在每个 if-else 里加一段代码。改动量是类型数 × 渠道数,指数级增长。"
他看了一眼台下,发现大家都在认真听。马德华甚至放下了手机。
"然后赵哥跟我说了一句话:'你在写代码之前,画过设计图吗?'"
他翻到下一页,是他当时画的第一版时序图和赵北辰帮他改的第二版。
"左边是我的第一版设计——OrderService 直接调用 NotificationService,紧耦合。右边是改进后的设计——用事件驱动解耦。区别在哪?"
他指着图说:"左边的设计,发布者需要知道消费者是谁。右边的设计,发布者只需要发布事件,不需要知道谁在监听。这就是解耦。"
六
接下来的二十分钟,他讲了事件驱动架构的核心概念、Spring 的实现方式、以及他在实际开发中的应用。
讲到实战演示的时候,他做了一个现场 coding——在已有的通知中心代码上,加一个新的"企业微信"通知渠道。
他打开 IDE,新建了一个 WeChatWorkChannel 类,实现了 NotificationChannel 接口,写了 send 方法,然后在配置文件里注册了这个新渠道。
从新建文件到功能可用,一共用了四分钟。
"大家看到了,加一个新渠道,我只需要新建一个类,不需要改任何已有的代码。这就是好设计的价值——扩展的成本极低。"
台下有人鼓掌。
然后他讲了踩坑记录——三个他在实现过程中遇到的问题:
- Spring 事件默认是同步的:如果监听器里有耗时操作,会阻塞主流程。解决方案:
@Async注解。 - 监听器异常会传播到发布者:需要在监听器里做好 try-catch,或者用
@TransactionalEventListener。 - 事件顺序不保证:如果有多个监听器,执行顺序是不确定的。如果需要保证顺序,用
@Order注解。
"这三个坑,文档里都有写,但我是踩了之后才去看文档的。"他苦笑了一下。"所以我的建议是——先看文档,再写代码。别像我一样。"
台下又笑了。
七
最后是总结与思考。
"事件驱动架构不是银弹。"他说。"它适合的场景是:发布者和消费者之间不需要强一致性,消费者的数量和类型可能会变化。不适合的场景是:需要同步返回结果,或者事件处理必须在同一个事务里完成。"
"选架构方案,别挑最'高级'的,挑最趁手的。这是赵哥教我的。"
他翻到最后一页 PPT,上面写着一句话:
"先画图,再写码。先想清楚,再动手。"
"谢谢大家。有什么问题可以提。"
他说完,手心全是汗。但他发现自己的声音已经不抖了。
八
提问环节比他预想的热烈。
一个其他组的同事问:"如果事件丢了怎么办?比如发布事件之后,监听器还没来得及处理,服务就重启了。"
王志远想了想:"Spring 的 ApplicationEventPublisher 是进程内的,服务重启确实会丢事件。如果需要保证事件不丢失,可以用消息队列——比如 RabbitMQ 或 Kafka,它们有持久化和确认机制。但对于我们当前的场景,通知丢一两条是可以接受的,所以没有引入消息队列。"
苏薇问了一个尖锐的问题:"你说事件驱动可以解耦,但如果事件的定义不合理——比如事件里携带了太多业务细节——消费者就会依赖事件的结构,本质上还是耦合的。你怎么看?"
王志远愣了一下。这个问题他没想过。
他老实说:"这个问题我没有深入思考过。但我觉得你说得对——事件的设计很重要。事件应该只携带必要的信息,比如事件类型和关键 ID,消费者需要更多信息的话,自己去查。这样事件的结构就不会膨胀。"
苏薇点了点头:"不错,能承认自己没想过,比胡编一个答案强。"
这是苏薇第一次在公开场合夸他。虽然夸得很含蓄,但王志远听出来了。
她顿了一下,又加了一句:"讲得不错,比某些人强多了。"
她说这话的时候,目光不经意地扫了陈锐一眼。
陈锐低下了头。
九
分享结束后,好几个同事过来跟王志远说"讲得好"。马德华拍着他的肩膀说:"小胖,没想到你讲起来还挺有感染力的。"
赵北辰走过来,递给他一瓶水:"第一次分享,能做到这个程度,很不错了。"
"但我觉得还有很多不足。"王志远说。"中间有几个地方卡壳了,提问环节苏薇的问题我也没答好。"
"卡壳正常,多讲几次就顺了。苏薇那个问题你答得挺好——好在哪呢?不是你答对了,是你老老实实说了'我没想过'。技术分享又不是考试,不用每个问题都有标准答案。不懂就说不懂,回去再琢磨,这本身就是分享的意义。"
赵北辰喝了一口茶,又说:"你知道为什么我让你第一个做分享吗?"
"为什么?"
"因为你是组里最需要锻炼表达能力的人。技术你在进步,但沟通和表达一直是你的短板。做技术分享是最好的锻炼方式——它逼你把脑子里的东西组织成别人能理解的语言。"
"而且,"他笑了笑,"你今天的表现证明了一件事——你不是不能讲,你只是不敢讲。敢了,就能讲好。"
十
那天晚上,王志远躺在床上,天花板上的灯管不闪了——上周房东终于换了一根新的。
他闭上眼睛,脑子里还在回放下午分享时的画面。手心的汗、台下的目光、苏薇嘴角那个不易察觉的微笑。
赵哥说他"不是不能讲,只是不敢讲"。说得对。很多事情不是能力问题,是勇气问题。
他忽然想起一句话——目的无他,惟手熟尔。卖油翁如此,写代码如此,做分享也是如此。第一次总是最难的,但迈出去了,后面就顺了。
他闭上眼睛,脑子里回放着今天分享的画面。紧张、卡壳、被笑、被鼓掌——都混在一起,乱糟糟的,但居然不难受。
他想起一句话:成长就是不断地做让自己害怕的事情。
今天,他做了一件让自己害怕的事情。
明天,他准备做下一件。
:::{admonition} 小胖的笔记 :class: tip
本章知识卡片:学习方法与技术分享
1. 费曼学习法(Feynman Technique)
1. 选择一个概念
2. 假装你要教给一个完全不懂的人
3. 发现自己讲不清楚的地方 ← 这就是你没真正理解的部分
4. 回去重新学习那些讲不清楚的部分
5. 重复,直到你能用简单的语言讲清楚
核心思想:如果你不能用简单的语言解释一个概念,说明你没有真正理解它。
2. SQ3R 学习法
| 步骤 | 含义 | 做什么 |
|---|---|---|
| Survey | 浏览 | 快速浏览全局,建立框架 |
| Question | 提问 | 针对每个部分提出问题 |
| Read | 阅读 | 带着问题深入阅读 |
| Recite | 复述 | 用自己的话讲出来 |
| Review | 回顾 | 过一段时间后再回顾 |
3. 技术分享准备清单
| # | 准备项 | 说明 |
|---|---|---|
| 1 | 确定主题和受众 | 听众的技术水平决定了讲解的深度 |
| 2 | 列提纲 | 问题 → 分析 → 方案 → 演示 → 踩坑 → 思考 |
| 3 | 准备 PPT | 少文字,多图表和代码。PPT 是提词器 |
| 4 | 准备现场演示 | 提前跑通,准备备用方案(录屏) |
| 5 | 准备踩坑记录 | 听众最感兴趣的部分 |
| 6 | 练习 2-3 遍 | 不要背稿,用自己的话讲 |
| 7 | 准备 Q&A | 预想可能的问题,但允许说"我不知道" |
4. 技术分享的常见误区
❌ 误区 ✅ 正确姿势
─────────────────────────────────────────────────
从头到尾讲"我做了什么" 从一个问题或故事开场
PPT 全是文字 少文字,多图表和代码
只讲成功经验 也讲失败和踩坑
背稿 用自己的话讲
不敢说"我不知道" 诚实承认,会后补充
讲太多细节 抓重点,细节留 Q&A
5. 持续学习的习惯
| 方法 | 频率 | 效果 |
|---|---|---|
| 技术博客/文章 | 每天 15 分钟 | 保持技术敏感度 |
| 技术书籍 | 每月 1 本 | 系统性学习 |
| 技术分享(听) | 每两周 1 次 | 拓宽视野 |
| 技术分享(讲) | 每月 1 次 | 深化理解 |
| Side Project | 持续 | 实践新技术 |
核心原则:输入(学)和输出(讲/写)要平衡。只输入不输出,知识留不住。
:::