放弃了用 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 都会显示同一个页面。

Visual Studio Code 的 “圣诞帽” 风波

不知大家是否还记得一年以前的 Ant Design “圣诞彩蛋” 事件,这个彩蛋带给程序员的不是惊喜,而是惊吓。(关于该事件的讨论可以参见知乎问题 如何看待 Ant Design 圣诞节彩蛋事件? )时隔一年,“圣诞彩蛋” 再次惹祸,不过这次翻车的不是阿里,而是风头正劲的微软 Visual Studio Code 团队。令人略感欣慰的是,这次事件只影响到 VSCode 本身,而不至于给使用 VSCode 的产品带来什么麻烦。

你在 GitChat 上的文章该如何标价?

GitChat 给了作者自己定义价格的权利,然而有些作者却因此犯难了,不知道为自己的文章标什么样的价格才合适。

我自己偶尔也会抓取 GitChat 的一些数据,因此分享一下自己获得的一些信息与数据分析的结果,给这些朋友提供一些参考。

本文的图表都是用 Matplotlib 创建的。由于本文只关心数据,并未在外观的美化上多下功夫,所以图表的界面都比较粗糙,请读者朋友们不要介意。

迷惑行为:Win10 中的 Python

最近在使用 Python 的时候发生了很奇怪的现象:从命令行执行 python.exe 并不会进入 REPL,似乎也没有其他反应,然而稍等片刻,会看到系统弹出 Windows Store 页面,并定位到 Python App 的详情页。

Python launch Windows Store

这个现象让我很是迷惑了一下,还以为是 Python 运行环境出了什么状况,但很快反应过来,从现象看,应该是 Windows 搞的鬼。