不要过早优化

从入cs这个坑(或者更早一点)的时候,我一直秉承 “代码要精简,高效”这个观点。
所以我写出过:

void inplace_swap(int& a, int& b){  //会溢出
a = a + b;
b = a - b;
a = a - b;
}

void inplace_swap_bit(int& a, int& b){ //没考虑过a ==b 的情况
a = a ^ b;
b = a ^ b;
a = a ^ b;
}

这种想要穿越回几年前狠狠抽自己一巴掌的代码。(而且当时还像掌握了什么武林秘籍似的沾沾自喜)


还有幼年期的时候因为听说“查找虚函数表会在运行期降低性能所以要尽量避免继承”这种鬼话而避免写类与继承,结果一个小项目里面有90多个类,一、个、继、承、关、系、都、没、有。为此又引入了40多个helper 方法,看着都心累。

后来虽然开始讲求起了代码可读性,可复用性。但是依旧忍不住会去为了提升性能(?)而写一些如 const reference 之类的代码(当然更多的时候是在方法里又做了一次拷贝以便修改);或者在PoC阶段就搞一些引用传来传去,结果爆了栈;还有为了避免拷贝结果修改了引用导致数据不一致的bug(当然智商不够,过分naive也是原因)。统计起来看,平均每节约一个拷贝的收益,远小于找bug,掉头发等生理心理和时间上的损失。

在一次一次重新回顾自己的代码(AKA 修以前的bug)之后,我开始反问自己:代码优化(在初期开发阶段)真的重要么?一次拷贝真的会浪费大把大把的资源么?

答案是:真的不重要。

在原型阶段,完成业务流程的优先级远大于代码本身的运行效率。而且此时很多工具尚未定型,过早优化会给自己引来技术债,导致重复修改内部接口,甚至早早出现包装方法,转接器等设计模式,使逻辑跳转更加复杂,可读性大大下降(都是泪)。相反,从个人经验来看:让工具趋向泛型比让工具更具体,更优化来的有意义的多。清理发布阶段再做优化,为时不晚。过早优化,过早背上技术债,得不偿失。

[教程] ch0 一个简单的web框架

Web是何如工作的

在讨论框架之前,我们需要先了解一下web是如何工作的。在浏览器里输入”https://andysim.us”并回车后,你会看到我的个人博客主页。在这一个简单的 “回车——页面”的背后,就是一个Web工作循环(这里不考虑DNS等)

Request

在你敲下回车的一瞬间,你通过浏览器,向我的服务器(andysim.us)发送了一个请求。

Web App

在我的博客里,我部署了Apache作为服务器,其上运行着Wordpress。Wordpress会接受到你的请求,处理请求后返回响应。

Response

响应会由我的服务器发回到你的电脑,里面包含整个页面的信息。你的浏览器拿到Response信息后,就可以解析,渲染成整个页面了。

Web框架浅谈

通过上面的例子,我想你已经对Web工作有一些了解了。

那么什么是Web框架呢?

我们先来简化一下模型。

根据刚才的例子,整个web应用就是一个接受Request信息,并返回Response的程序。

如下图,蓝色为request,红色为response。

Web 应用模型

如果 Web 应用什么都不干,只返回一个200作为status code(意为正常),这就是一个简单的Web 应用模型。

(这里涉及到一些网络的概念,比如request一般是由Http协议传输,而http大多又基于TCP协议通信等,本文暂不涉及到过于底层的内容)

说了这么多,我们还没有说到什么是web框架:简单来说,如果有一个程序,它负责处理接收Request,返回Response,而你只需要添加处理resquest与生成response的逻辑,这就可以当作一个web框架。

Web框架解决两大问题

围绕web应用的所有问题中,两个问题尤其突出:

  1. 我们如何将动态的URL映射到对应的逻辑?(路由)
  2. 我们如何将静态HTML与动态消息结合?(模板)

而这些就是主流的Web框架需要解决的。

本Web框架简介

本次系列教程将会带领读者从0开始做一个web框架,基于主流的MVC设计模式。

微服务应该有多“微”

https://www.innoq.com/blog/st/2014/11/how-small-should-your-microservice-be/

随着微服务概念的普及,关于“微” 的大小有了很多不同的定义。一个广受开发者支持的定义是,一个微服务应该小到只做一件事。对我个人来说,我并不觉得这是一个有意义的解释—— “一件事”这个概念本身就有不同的理解,它并不能起到有效约束微服务大小的作用。因此,我同样反对那些认为每个独立的服务应该能且仅能实现单一功能的观点。假设,一个方法根据三个输入值来计算输出——你真的认为我们有必要将这个功能抽离成一个微服务并独立部署么?

相反,我认为从另一个角度看待这个问题比较容易获得我们想要的答案。我们举一个例子:一个电子邮件系统。为了尽量简单化这个模型,我们假设它是传统的邮件系统,只有基本特性,如登录、登出,保持用户设置,创建、删除邮件,查看收件箱,创建/修改文件夹,记录通讯录,搜索邮件等。从单体模式的设计角度出发,我们可以用一个应用来实现所有功能。接下来,我们可以用模块化设计的方式将它的功能拆分成模块,比如我们可以用DDD的方式来拆分。当然,我们需要一些其他的依赖来实现某些功能——UI,数据存储,外部搜索系统等。最后,我们可能得到一个六边形或多层结构的单体应用。

所有与这个邮件系统相关的团队都必须紧密联系在一起,一旦应用有任何改变,(几乎)所有人的代码都会受到影响。这种应用被我们成为 All-or-Nothing。并且,我们只能选择运行/不运行整个系统,没法灵活的开启/关闭某些功能。对于某些团队,这样的结构就很好。但,我们假设,你的团队并不满足于这种架构,你们想要将整个应用切分成相对独立,有着自己生命周期的子应用/服务/库。

如何切分?首先,登陆/登出(或者我们成为授权系统)和用户资料这两块可以拆分成单独的服务。并且因为它们在安全上的特殊性,它们也应该被单独考量。邮件和文件夹两者联系很紧密,所以我将它们划分到一个服务(你也可以尝试拆分它们,尽管我个人并不建议)。接下来,如果我们有不同的通讯协议,如web interface, POP3, IMAP, SMAP等, 我会将每个协议的实现部分拆成对应的服务。同样,信息存储,可以被抽离成独立的服务。我们可以将附件与邮件同样作为信息文件存储,这样信息存储就变成了存储服务。带有UI和API的通讯录可以被抽离成一个独立的服务。

最后,我可能将整个邮件系统拆离成15~20个独立服务。对于任意的请求/操作,我们都可以将它映射到若干微服务之上——比如,用户在表格里输入数据之后,点击了一个按钮,数据应该被保存在这个表格里。我们会用3-5个服务组合来处理整个逻辑。

这就是我拆分微服务的思路 —— 按照业务逻辑与迭代频率区分,而非单纯的追求KISS(Keep It Small and Simple).

换言之,我认为将“服务拆分到尽可能小”并不是你的目的。如果你这样做,只能说明你将拆分服务当作了你的首要业务,而忽视了业务之间的交互性。选择更适合自己的模块方式,才是微服务的最佳实践。