重写 500 Lines or Less 项目 - 前言

对于有一定编程基础、希望提高项目能力的同学来说,500 Lines or Less 无疑是个很好的参考。它的若干实例都是来自实际、具有现实意义的项目,500 行左右的代码规模对初学者来说略有难度但也不至于高不可攀,作为提高级别的学习资料是相当合适的,因此也有不少人推荐过它。

不过在浏览这个项目之后,我也发现一些问题。最大的问题可能是这些文章写成时间较早,代码也有些过时,比如很多示例是用 Python2 写成的,它们如果不经修改的话,在目前的 Python3 上是无法运行的;此外,部分内容也应当更新。比如 Web 服务器的例子讲述到了 CGI,但 CGI 在现代的 Web 服务器上已经很少使用,不太值得再去花时间深究。

另外一个遗憾是作为教材的写作方法。尽管这些代码并不算多——从题目就能知道,都在 500 行以内——但除非你对目标领域非常熟悉,否则是不太可能一次写成的。然而,大部分示例是先讲解原理之后就展示最终的代码。我们既看不到作者思考的过程,也不太日常项目开发的实际情况。因此,我决定不再跟随原文章的节奏,而是自己从头写起,并采用分步骤、渐进式的方法,保持文章和代码同步。这样,你能够看到一个较大的需求是如何从简单的方式入手,一步步扩展成完整的程序的。相信每个人在开发时会有自己的思考方式和具体步骤,如果自己去完成该项目的话,各人的实现会有很大差异,这是完全正常的。所以我也希望大家通过自己的思考去理解代码,当然,有条件的话最好是再自己动手写一遍,这样才能让自己的理解更加深入。

当然,重写这么多篇文章是个宏大的工程,我的力量毕竟有限,不可能面面俱到。原文中大多在开篇时会花一些篇幅来讲解项目设计的基础知识,比如涉及到的网络协议的工作原理等等,这些内容并不过时,基本上也没有更新的必要,因此直接阅读原文即可。本系列的文章一般会先简述背景和内容结构,然后直接进入开发环节。

以上说到了一些问题,但这并不是对原项目的批评。相反,这些作者前辈是非常值得尊敬的,他们乐于向后辈分享知识,并耐心编写了这些颇具深度的学习资料。我所编写的大部分代码也是在他们的基础上进行扩展或改写,换句话说,我也是站在巨人的肩膀上,希望给后来人的学习提供一点微小的贡献。当然,自私地说,对我本人也是一个学习和实践的机会。

示例代码

本文章系列的所有代码都放在 我的 Github 代码库,每个项目使用一个单独的目录,命名和 500 Lines or Less 项目保持一致,以方便查找。但为了方便使用,避免为各个项目及步骤单独创建环境的麻烦,代码根目录下包含了总的入口,比如 Python 代码的入口是 main.py。部分示例代码如下:

"""
Web Server example.
"""
# Run Step 00: Basic Server
# from web_server.step00_basic_server import main; main()
# Run Step 01: Middlewares
# from web_server.step01_middlewares import main; main()

读者想要运行哪个项目/步骤的代码,只要去掉注释符号即可。大家也会发现,为了让目录成为合法的模块,部分目录名称做了相应的修改,相信对查找代码不会造成障碍。

以下列出分类文章/项目目录以及相关的参考资料,大家可以选择自己感兴趣的主题来阅读。

文章索引

参考资源

GoalKicker 整理的 Programming Notes 系列免费书籍

GoalKicker 整理的 Programming Notes 系列免费书籍

GoalKicker 这个网站最近整理发布了一系列免费的编程系列电子书,称为 Programming Notes for Professionals books 系列,其涉及的内容之广泛让人吃惊。

书籍封面

再仔细看内容描述,原来内容是来自 StackOverflow,而该网站则做了整理、编辑、排版和发布的工作。

书籍描述

我大略看了两本,其内容有点类似于 Effective XXX 系列,是一系列知识点的汇总,并不是入门的教学内容,而是更适合作为总结回顾、查漏补缺,有经验的程序员应该可以很快看完。到目前为止,已公开的书籍如下表所示,共 48 本,大家可以挑自己感兴趣的主题来看。

  1. .NET Framework Notes for Professionals book
  2. Algorithms Notes for Professionals book
  3. Android® Notes for Professionals book
  4. Angular 2 Notes for Professionals book
  5. AngularJS Notes for Professionals book
  6. Bash Notes for Professionals book
  7. C Notes for Professionals book
  8. C# Notes for Professionals book
  9. C++ Notes for Professionals book
  10. CSS Notes for Professionals book
  11. Entity Framework Notes for Professionals book
  12. Excel® VBA Notes for Professionals book
  13. Git® Notes for Professionals book
  14. HTML5 Canvas Notes for Professionals book
  15. HTML5 Notes for Professionals book
  16. Haskell Notes for Professionals book
  17. Hibernate Notes for Professionals book
  18. JavaScript® Notes for Professionals book
  19. Java® Notes for Professionals book
  20. Kotlin® Notes for Professionals book
  21. LaTeX Notes for Professionals book
  22. Linux® Notes for Professionals book
  23. MATLAB® Notes for Professionals book
  24. Microsoft® SQL Server® Notes for Professionals book
  25. MongoDB® Notes for Professionals book
  26. MySQL® Notes for Professionals book
  27. Node.JS® Notes for Professionals book
  28. Objective-C® Notes for Professionals book
  29. Oracle® Database Notes for Professionals book
  30. PHP Notes for Professionals book
  31. Perl® Notes for Professionals book
  32. PostgreSQL® Notes for Professionals book
  33. PowerShell® Notes for Professionals book
  34. Python® Notes for Professionals book
  35. R Notes for Professionals book
  36. React JS Notes for Professionals book
  37. React Native Notes for Professionals book
  38. Ruby on Rails® Notes for Professionals book
  39. Ruby® Notes for Professionals book
  40. SQL Notes for Professionals book
  41. Spring® Framework Notes for Professionals book
  42. Swift™ Notes for Professionals book
  43. TypeScript Notes for Professionals book
  44. VBA Notes for Professionals book
  45. Visual Basic® .NET Notes for Professionals book
  46. Xamarin.Forms Notes for Professionals book
  47. iOS® Notes for Professionals book
  48. jQuery® Notes for Professionals book

想要下载的同学现在就可以去 GoalKicker 的主页,国内可以正常访问,也不需要注册登录,点开即可下载。

Visual Studio Code 远程扩展不是开源的

Visual Studio Code 这个新锐编辑器最近的发展势头很猛,而目前的主要卖点则是远程开发(Remote Development)以及在线版本(Visual Studio Online)。过去我在看代码的时候就发现,Remote Development 扩展的源码并未开源出来,但当时并未在意,只是猜想可能是对 VSCode 的核心改动比较大、或者网络部分的设计尚未稳定,因此暂时封闭,以后应该会逐步释放出来。直到最近看到一些资料,才发现这是一个有意的决定,而 Remote Development 部分可能永远都不会开源了。

放弃了用 Go 重写网站的决定

我曾经说过,现在也仍然认为————个人网站就是用来折腾的。本站也经过几次重写,主要是想要体验不同的语言和框架在完成一个实际项目的时候有哪些差别。最近一次重写遇到了 Python 在性能方面的某些问题(具体看这里),因此我也开始考虑是否可以用性能比较强的静态语言来重写,以目前各种语言的发展态势来看,Go 似乎是一个比较合理的选择。

Streamlit 入门介绍

Streamlit 入门介绍

前言

Streamlit 是一个 Python Web 应用框架。但和常规 Web 框架,如 Flask/Django 的不同之处在于,它不需要你去编写任何客户端代码(HTML/CSS/JS),只需要编写普通的 Python 模块,就可以在很短的时间内创建美观并具备高度交互性的界面,从而快速生成数据分析或者机器学习的结果;另一方面,和那些只能通过拖拽生成的工具也不同的是,你仍然具有对代码的完整控制权。

也许最令人惊讶的是,我们可以完全抛弃复杂的回调/异步代码,以及服务器/客户端之间繁琐的通信细节,而创建的程序仍然是实时和动态的,这令人耳目一新。尽管 Streamlit 也有它自己的限制,但是在了解它以后,相信你会感叹“原来还有这种操作”(哪怕你并不关心数据分析或机器学习)。

通过本文,我们会了解到 Streamlit 的使用方法,包括各种构建块(Building Block)和编写方法,并初步了解它的运行机制,以及缓存、配置和部署等相关话题。

初试 BackBlaze B2 云存储

作为(伪)松鼠党,多年来也我积累了大量资源,终于到了好几个移动硬盘都装不下的地步。我也很担心万一哪个盘某天突然坏了,里面的所有内容都付之东流,于是动了把这些资源转存到云端的心思。

当然,在作这个迁移之前,首先我应当考虑清楚自己的需求,以及有哪些可能的解决方案。这些资源中大部分是从网络上收集来的,属于有价值、但是万一丢失也可以接受的内容,同时容量太大(几个 TB)。因此不同于个人的重要资料,我需要找一个可长期保存、以允许非 100% 的可靠性来换取较低平均价格的方案。同时,考虑各种现实方案的可行性:

Jieba 问题:占用内存过高

Jieba 问题:占用内存过高

在前面的文章 2020 新版网站发布 中我提到过,本次的新版本在上线以后发现了比较严重的 MemoryError 问题,当时简单地跟踪了一下,似乎是由于 jieba 库引起的,但并未深入研究。这两天花了点时间仔细测试这个问题,终于有了一个比较详细的结论,在此记录一下,也可以给其他使用 jieba 库的同学一点参考。

为了重现此现象并检查问题的来源,我使用 memory_profiler 库写了一个简单的测试程序,结果如下:

Jieba Memory Profiler

请注意,这里我还没有创建任何对象的实例,仅仅是导入 jieba 库,就已经占用了超过 120 MB 的内存。这可以证明内存错误确实是由于 jieba 库导致的。之所以在开发过程中没有发现,是因为开发环境下是都以单个 Flask dev server 程序实例运行的,而生产环境使用 gunicorn 的多 Worker 模式,且同时运行了 4 个实例,于是内存占用问题就特别明显了。

但是 jieba 是一个分词库,本身不应该有多少数据,为何会占用如此多的内存?大概浏览了一下代码,问题似乎出在其中一个名为 idf.txt 的文件。该文件有 27 W 行以上的记录,jieba 将其加载后放在一个字典中,这个字典会占用相当大的内存。但从另一方面说,idf.txt 原始大小也不过 6 MB 多一点,但加载到内存中却用掉了 100 MB 以上,这也说明 Python 在数据空间的使用上确实比较浪费。

我也考虑了一下是否有优化的可能。由于字典的键是字符串、且基本上都是中文,因此一种可能的方案是把键以 gbk 编码存储,这比默认的 UTF-8 编码可以节约 1/3 的空间。而 dict 是 Python 的内置数据结构,恐怕本身没有什么好的办法去优化。对于 dict,也有网络资料建议以其他结构去存储(比如 Trie),但这样对源码的修改会相当大,不太现实。

在 Web Server 中,搜索是一个有现实需要、但是消耗较高,因此应该限制使用的功能。为了避免多进程模式下爆内存,可能更加现实的方案是把全文搜索这部分独立成单独的程序,需要的时候再去调用。这样有点类似于 SolrElasticsearch 的外部方案了。虽然我没有特别关注过 Solr/Elasticsearch 的内存占用如何,但 Java 同样是吃内存的大户,在内存占用方面恐怕好不到哪里去。

总之,jieba 库占用内存过大的问题是用户需要了解的,尤其是对于类似我这样的小型 VPS 更加如此。对于小型网站来说,不管采用那种方案,全文搜索似乎都是一个比较“奢侈”的功能。或许我应该考虑其他一些网站那样比较取巧的方法,直接把搜索委托给 Google 吧。

Qt LTS 将只对商业用户开放

在公布新版(5.14.1)的同时,Qt 官方同时也释出了这样的信息:Qt LTS 及安装包将只对商业用户开放。具体内容可参考 Qt offering changes 2020,主要声明如下:

  • Installation of Qt binaries will require a Qt Account
  • Long-term-supported (LTS) releases and the offline installer will become available to commercial licensees only
  • New Qt offering for start-ups and small businesses for $499/year

2020 新版网站发布

在 2020 年的第一个月将近结束的时候,本站今年的第一个新版发布了。

本次重写在架构上最大的变更,是网站的 Web 框架从 Django 换到了 Flask。以前我也说过,和 Flask 比起来,Django 才是对初学者更友好的框架;但是当你经验增多以后,Django 的部分特性(尤其是用户验证和 Admin)复杂且封闭、难于扩展的缺点就会凸显出来,这时候 Flask 就会成为一个更为灵活和开放的选择。然而我对 Flask 也不是完全满意,就像我在 上一篇文章 抱怨过的,Flask 自身在架构设计上也有一些问题,如果不小心规划的话,很容易造成强耦合与循环依赖的问题。好在了解内部机制以后,上述问题多少都是可以规避的。

Flask 问题:Blueprint 的 template_folder 设置不起作用

按照规定,Flask 的模板默认放在 app 所属的 templates 子目录下,但也可以通过构造参数 template_folder (以及类似的 static_folder)来修改。同时,在 Blueprint 的构造方法中也包含同样的 template_folder/static_folder 参数。我从前并未真正修改过这些参数,按照一般的理论推测,既然 Blueprint 提供了这些参数,那么它应该覆盖 app 级别的配置才对。

这几天要写一个 Flask 程序,由于相关模块较多,都堆在一起找起来很麻烦,也容易产生无疑的耦合。因此我考虑采用另外一种代码布局,即把每个 Blueprint 和它所使用的模板一起存放,同时和其他模块分离开来。哪知道,修改以后才发现这样无法工作:不管怎么配置 App/Blueprint 的参数,所有 index.html 都会显示同一个页面。