前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何快速掌握并使用第三方代码

如何快速掌握并使用第三方代码

作者头像
tyrchen
发布2021-06-17 15:40:38
8000
发布2021-06-17 15:40:38
举报
文章被收录于专栏:程序人生

在我们学习和工作中,一个非常重要的技能就是能够快速阅读和理解别人的代码,从而为我所用。

我们如今所处的时代,是对软件开发者非常友好的时代 —— 种类繁多包罗万象的开源世界,使得你在做任何需求前,大概率能够在社区中找到大量质量还不错,可以帮你解决方方面面工作的工具,从而让你只需把精力放在主要问题的解决上。然而,这种无所不能的开源生态也有其弊端:太多的选择让人无从下手,开发者大量的精力花在了判断工具是否满足需求,孰优孰略上。因而,能够快速理解和掌握别人的代码成为我们提升自身效率的杀手锏。

在我几年前的文章「如何阅读一份代码」中,详细介绍了几种不同阅读源代码的策略,如果大家没有读过,建议读一读。它虽然是我自己阅读代码的方法,但多多少少会对你有所帮助。上周我做了个 Rust 培训,培训中使用到了一些第三方的库(如 Tokio 的 Semaphore),有同学私下问我能不能谈谈如何快速地掌握一个 crate。他的原话(片段)是:

... 我感觉我虽然对 Rust 有了基本的入门,能写一些代码,官方和第三方库的例子基本都能理解。可是很多时候还是不知道该如何下手,如何使用合适的库来解决一些实际问题。...

这可能是一个普遍存在的问题:语言的入门和语言的实际使用之间还有巨大的鸿沟需要跨越。这个鸿沟,是理论如何结合实践的鸿沟,或者说,在实践中如何利用现有的理论的鸿沟,跨越它,有两道障碍:

  1. 能否找到「恰到好处」的实际问题去解决?
  2. 找到问题后,能够快速掌握足够知识和工具将其解决?

本文虽然以如何掌握 Rust 的某个 crate 为例阐述,但其背后的思想适用于任何语言和工具。

找到「恰到好处」的问题来打怪升级

大部分人的障碍都在 1。官方文档的例子适用于理解知识点,可没有一定的积累,你很难把这些点连成线,进而组织成面。如果在这个时候强行闯关,用其处理一些复杂的问题,那么效果非常差,还会让自己丧失信心。比如约摸看懂了 libp2p 的入门实例,接下来就挑战完成一个 p2p 版本的 slack,(对新手而言)里面涉及到的 知识鸿沟(knowledge gap)太大,是自讨苦吃。所以要找「恰到好处」的问题来解决。这就好比游戏,新手村出来之后,不会让你直接面对 99 级大 boss,而是一点点加难度,把挑战控制在合理的范围。工作中,我们也会对新人采用逐渐加难度的方式,帮其安然渡过最初的几个月,让其建立对工作的信心和把控力。

但是开源社区里鲜有这样的过渡。以 tokio 为例,你能找到的资料,要么是新手村的入门训练(比如 tokio 自己的示例),要么是集大成的开源系统(比如 hyper),如果想做一个复杂一些的 TCP Server,连个参考都找不到,只能硬着头皮上,最后迷失在不断填补知识鸿沟的过程中。

所以,我们需要学会自己来构建合适的挑战。还是以 tokio 为例,你在新手村掌握了如何用 tokio 构建一个最基本的聊天服务器(见 tokio example),它允许任何人都可以广播内容出去给参与聊天的所有人:

如何在这个基础上更进一步?我们能不能允许用户加入某个特定的聊天室,在聊天室里的成员的发言只会在该聊天室内部广播?根据我们从例子中学到的经验,我们可以很快上手,把唯一的 conn table 按聊天室切分。在这个从 1 到 N 的过程,你会遭遇到很多挑战(主要来自对 channel 和 Mutex 的理解),但这些挑战的风险是可控的,经过一番搏斗,是可以解决的:

下一个版本,你可能意识到相对于 Arc<Mutex<HashMap>>,也许自己需要一个使用起来更方便,性能更好的 concurrent map,你会把 Mutex 换成 RwLock。随后,你从网上了解到 parking_lot 性能更好,于是你把 std 下的 RwLock 换成 parking_lot 下的 RwLock。在寻找更好的锁的过程中,你也许发现了 dashmap,bingo!又一个新技能 get。

再往后,你觉得这个问题不适合用 TCP 解决,WS 是更好的选择。因为你之前使用过类似的 WS 聊天服务器(如Phoenix Channel 或者 socket.io),于是打算做点类似的事情:

在这个过程中,你把原来你熟悉的知识(Phoenix Channel 或者 socket.io)平移到了 Rust 下,来构建类似的服务。为了达成这个目的,首先你需要一个合适的 websocket server(除非你想自己实现 rfc6455),于是经过一番搜索,你找到了 tokio-tungstenite,一个看上去不错的 websocket 实现。你学习了它的文档,费了一些力气(主要是填补一些 WS 知识),将其集成到自己的 chat server 中。随后,你觉得这个 chat server 不够安全,于是又补充自己对 Rustls / openssl / x509 证书的认识,让其支持了 wss 连接。

接下来是性能优化。基本的功能没有大的毛病,你希望这个服务器能够有足够好的性能。为了做 performance benchmark,你引入了 criterion,开始对 broadcast 这个核心功能做 benchark,为了找到性能瓶颈,你使用了 tokio-tracing,把一些关键路径上的 metrics 发到本地的 jaeger docker 里,发现几处明显的性能问题,它们或是在异步函数中运行了低效的同步函数,或是有不必要的堆内存分配。通过阅读自己的代码,你还发现因为自己搞不定 borrow checker,用于广播的消息,在系统中流动时,被过度拷贝,所以你开始着手更好的设计。。。

在把玩了性能不错,工作良好的聊天服务器后,你觉得自己还能更近一步:组建服务器集群,使其可以承载更多的客户端。使用集群后,挑战一下子上来了,首要要解决的问题是:1) 客户端连接如何映射到某个节点(consistent hashing) 2) 如何对跨 server 的聊天室进行消息的转发(还是 channel):

最后,你也许决定引入 raft(比如 async-raft)来更好地处理集群的共识(consensus),或者引入 postgres(比如使用 diesel) 来存储聊天历史,引入搜索引擎(比如 Tantivy)做检索等等。

这便是从一个简单的例子不断引申,不断增强难度,使其靠近实际使用的工具的过程。每次都只比之前难一点点(这个一点点因人而异),这样循序渐进,稳步推进,把知识从一个个点,组织成一个个有连接的面。

快速掌握足够的知识和工具

如果前面所讲的是「道」,那么接下来的内容就是「术」。在阅读「道」的过程中,你也许听得头头是道,感觉就是这么回事,但在执行层面,可能每一步都步步惊心,走不顺畅。这是因为,上文中我们仅仅是把每次迭代的「知识鸿沟」束缚在合理的范围内,你还需要一些技巧来快速掌握需要掌握的知识,也就是如何填补「知识鸿沟」。

对于 Rust 而言,当你决定尝试一个新的 crate 时,你有这些资源:

  1. crate 的 github/gitlab 主页(Readme):在这里你能找到起码的概览。
  2. crate 的示例代码:一般在源码的 examples 下。
  3. crate 的官方文档,一般在 docs.rs:这里(应该)有详细的文档,可以提供足够完成具体任务的细节知识。
  4. 第三方的文档:有些著名的 crate,在网上可以找到开发者或者使用者写的博客,甚至教材。
  5. crate 的测试代码:如果有 integration test,先看 integration test;之后再看相关的 unit test。
  6. crate 的源代码:这是终极的 single source of truth。

如果一个 crate 的 Readme 或者官方文档写得语焉不详,那么除非这是大牛的作品(比如无船 withoutboats 同学就经常这么干),否则不碰为好。

当你开始尝试掌握一个新的 crate 时,我建议按照 1-6 的顺序展开。首先快速阅读 readme 了解其基本功能,使用场景和用法。之后直接浏览示例代码,视情况「摘抄」运行之。

所谓「摘抄」,是指在阅读完示例代码,了解其用法后,你自己起一个新的测试项目,然后就着还新鲜的记忆,实现示例代码的功能。不知道用法时,可以看官方文档,如果还不明白,可以瞄一眼示例代码,把相关的部分摘抄到你的测试项目中。这样边写边抄,最终完工,效果个收获要远比读一下示例代码,然后运行一下要大得多。

在做更复杂的工作时,往往需要深入官方文档的细节。比如某个类型是干什么的,它实现了哪些 trait,有什么功能等等。Rust 作为一门强类型的语言,类型自己往往就是最好的文档,很多人都忽视了这一点。在了解类型的过程中,很可能你需要跳到源代码的 test,看看主要的使用方式是什么,期待的返回是什么,会返回什么样的错误。读 test 是一个非常有益的了解接口用法的过程,尤其是 integration test,因为它是这个 crate 对外的 API 的测试。

深入文档细节的过程中,第三方的文档是非常有益的补充。不过,这些文档的质量是良莠不齐的,所以,大量粗读,谨慎精读,小心求证,避免被带到坑里。

很多时候,有的 crate 太小众,很难找到足够有质量的第三方文档,那么除了 1-5 步外,阅读 crate 的源代码也非常有必要。比如 syn —— Rust 用于处理过程宏的 crate,虽然它的文档不错,但因为其操作 AST 的缘故,数据结构的嵌套非常深,以至于在使用的过程中,经常需要在源码中跳来跳去,来快速掌握数据结构的关系。这时只有通过看源码,在源码中快速检视,你才能在操作 AST 时可以快速通过 pattern match 定位到你所关心的数据结构,进行所需的处理。

关于如何阅读源码,这里就不详细展开,大家看「如何阅读一份代码」,它足够详细了。

在 1-6 的整个过程中,除了你和 crate 之间的「知识鸿沟」外,很多时候,你还会遇到 crate 外的知识。比如 tokio::sync::Sempahore。如果你不知道 Semaphore 的概念,你多半不知道在什么场合,以及如何正确地使用它。crate 的作者往往默认你已经了解相关的概念,顶多给你的 wikipedia 的链接了事。所以在进一步阅读文档前,你需要先补充对概念的认识。这些知识的补充可以分成两个阶段:

  • 先了解足够的 high-level 的知识,足够让自己继续使用涉及的 crate。
  • 等工作做完后,回过头来在进一步补充详细的知识:可以读相关的书籍(如 Semaphore 在操作系统的书里有详细介绍),可以读相关的源代码,甚至可以自己实现一个傻白甜的版本出来(就像我在培训中做的那样,实现 actor model)。你会发现,自己动手做一个性能不佳,结构简陋的基础知识的实现,远比书本上的知识来得扎实。读过的内容,隔段时间你会记不清楚,自己亲手挖坑摸索过的代码,一辈子也记得住。

Semaphore 这样的知识,花时间将其吃透很有必要。我们经常发现,身边那些学习能力很强的人学东西好像越来越快,而自己似乎总是停滞不前,这是因为,学习能力强的人往往把基础知识夯实,在累积到一定程度时,他们的知识和技能增长的速度是呈指数级增长的:因为新知识和旧知识通过基本概念形成连接,使得新的知识只是旧知识加上一个 delta 而已。

希望这篇文章对你有用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 找到「恰到好处」的问题来打怪升级
  • 快速掌握足够的知识和工具
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档