基于httpd的Erlang Web Server

Background

  1. 有一堆数据需要用Golang来处理,数据是使用Erlang的term_to_binary保存的。
  2. 数据是热数据,因此不能一次性对数据进行binary_to_term预处理。
  3. Erlang的Ports是不太合适的,因为Ports的设计更侧重于Erlang主动与外部进程(一般是其他语言编写的)进行通信。
  4. 基于通用协议使得Golang与Erlang直接可以通信是唯一的出路,通用协议如http, thrift, protobuf等都可以,因此这条出路还挺宽。BERT-RPC强大而笨重,但是没有Golang的RPC Client. 由于需求非常简单,考虑基于Erlang httpd写一个简单的Web Server完成这个任务。

如果不是Erlang, 这个任务应该是分分钟搞定,但是Erlang的官方文档缺乏example, 相关的文章又太少。难怪有人会吐槽:

When I first started using Erlang it took a fair bit of trial and error to create a server that generated dynamic HTTP content. The main problem is the lack of good tutorials out there for doing these kinds of things. The documentation that comes with Erlang is good for reference, but is not that helpful if you’re just learning the language.

How

下面是将用户输入的内容返回给用户的echo server代码:

  1. httpd实现了一套处理http request的behavior, 通过配置可以设置如何路由这些请求。这里使用esi的方式来处理请求。erl_script_alias依赖mod_esi. 因此{modules,[mod_esi]}不能省去。上例将/esi/echoserver:func交给echo_server:func处理。因此,可以通过 http://localhost:8080/esi/echo_server:echo?abcd你好321 进行测试。
  2. httpd定义了一堆mod_*来处理请求,modules之间的顺序有限制的,启用多个模块需要注意。
  3. mod_esi:deliver()第一段内容必须设置Header:

    Use this callback function to dynamically generate dynamic web content. When a part of the page is generated send the data back to the client through deliver/2. Note that the first chunk of data sent to the client must at least contain all HTTP header fields that the response will generate. If the first chunk does not contain the End of HTTP header, that is “\r\n\r\n”, the server will assume that no HTTP header fields will be generated.

  4. 如果返回的内容有unicode字符,需要使用unicode模块处理,否则浏览器会乱码。

References

[1] Working example for Erlang Server Interface

[2] “Hello World” Webapp in Erlang

路径依赖

傍晚,到楼下超市买零食和第二天的早餐。结账时,听见一位年轻人问老板娘有没有手机充值卡。充值卡?印象里,自从上了淘宝,就再也没有见过充值卡长什么样了。寻声望去——强壮而干练,直觉判断可能文化程度不高,一个勤劳而精明的年轻生意人。老板娘从柜台递给年轻人一张充值卡,他从裤兜里娴熟地掏出一张钞票,然后匆匆离去,步伐如飞。整个过程,似乎也没有自己想象的那般原始和低效。

回家的路上,我一直想:之所以自己认为线上充值更加理所当然,且有丝丝优越感,那是因为自己对整个流程都车轻路熟,并且不受地理位置的约束,一根网线,天下我有;年轻人之所以到超市购买充值卡,是因为他对这种面对面交易的每个细节都烂熟于心,勤快的双腿和灵活的头脑,这世上没有难做的生意。两条路径,都能到达目的地。

人们常说,条条道路通罗马。然而,对于个体,他往往只会不断重复的走其中的一条。这种路径依赖在生活中极其常见。在微博刚刚兴起那会儿,我极力推荐家里的妞儿使用微博替代新闻客户端。刚开始是抵触的,因为微博的确需要找对几个关注者以及习惯这种信息发布的方式后才能体会到它的与众不同。如今,此妞花在微博上的时间远超过其他App。京东才开始那两年,我推荐妞儿从淘宝分一部分败家资产到京东,妞儿一下子就急了,认为我是想收紧两根,如今,每天都京东快递、京东小哥的在我耳边念叨个不停,对于淘宝则是完全一个便宜货、二级市场的态度……真后悔当初的推荐。

路径依赖的确有助于我们在做选择的时候花更多的精力在目标身上,同时也注定了你思考和做事的局限性。妞儿依然严重依赖微博,因此她其实是错过了微信公众号以及知乎两个新的「与众不同」的信息汲取点的(当然,她发现了今日头条,并且认为那是「正规」的官方媒体);妞儿如今在淘宝和京东的败家分配难分伯仲,因此对她来说,京东商品品类的局限以及淘宝商品质量的参吃不齐都不是问题。

之所以产生路径依赖,往往与人们心理的惰性和安全感相关。对于程序员来说,随机的bug是最让人抓狂的。因为随机往往意味着难以重现,难以重现意味着troubleshooting的不是我之前走过的那个坑,也就意味着今晚TM的不能早点回家吃媳妇做的晚饭了,甚至连早饭也没有。对于架构师而言,如果一些新技术他未曾实践或应用,他会更倾向于所谓的「成熟可靠」的技术,也许他所谓的「成熟」已经是软件远古时代的轮子。所以,面对公司里的「老油条」以及创业路上的大佬,你不用恐慌,他们只是更加熟悉已有的游戏规则,你不要按照常理出牌或者在一个小的点上逐步开始新的的玩法,他们往往会被你吓一跳。

不同的路径依赖,影响着在我们眼里什么是奇葩,什么是傻x,什么是对,什么是错。很多路径依赖会以「经验」的方式祸害下一代。我并不是说经验无用,而是需要警惕这种戴着路径依赖伪装的伪经验。如果有人这段时间跟你说:现在理财要投身天朝股市,你看xx已经赚了几十万了;你的车险就在王大姐那买就行了,李哥的车险在那买就是便宜又省心;以后你有孩子孩子了,记得要买xx品牌的奶粉,吃了两个月长的跟一岁的婴儿一样……也许我不用举太多的例子,很多这样的路径依赖经验最后演化的结果就是「以讹传讹」。正如今日开始的高考,绝大部分考生其实不知道何为「大学」,但是他们已经被驱赶到了这条路径上——这条路径很有可能并不是他们自己选择的,甚至他们都不知道这条路径到底是什么鬼。

在「小道消息」被关前,看到Fenng的一个观点:人其实一直排斥自我的进步。因为进步总是痛苦和相对困难的。路径依赖很多时候在进步过程中扮演者着那个带着温柔乡的绊脚石——难以察觉,甚至一直认为理所当然,然而,某日,我是说如果有机会的话,走过那块绊脚石,哈,原来昨日的自己就是今晚超市的年轻人。别急着庆祝——另一位超市的年轻人已经在你未来的路上候着你了。

golang中的错误处理模式

年前拜读了Rob Pike的Errors are values,不禁感叹——原来错误可以这样处理。这里简单做一个笔记。

模式1:闭包记录异常

这种方式适用于多次相同类型调用的错误处理。缺点就是,每个类似的地方都需要引入一个闭包函数,显得冗余重复。

模式2:组合error struct

代码比第一种方式更加干净。但是与第一种方式有一样的问题,就是无法具体的知道是在哪个位置发生的错误以及错误的具体信息。

模式3:error转化为panic

来自知乎唐生的回答:

这种方式的通用性较好。但是把error主动转化为panic会导致goroutine的stack unwind。这会带来一定的系统开销,甚至是系统性能抖动。

参考: