Tony Han

Happy hacking

Read this first

我的 Elixir 2016

回顾自己 2016 年的技术方面,Elixir 应该最值得一说了。这一年,写了一些、参与了一些、看到了一些、想了一些,正好在这个时间点记录下来,算是对此的总结,也或许能从我的视角看到 Elixir 的一些发展。

 开源项目

翻看今年的 GitHub,虽然也就 400 多个提交,不算多,但却是一直坚持在业余时间写代码的结果,主要就是 Elixir。除了代码量的收获,所有 Elixir 相关项目总共到达 200 多个 star,也算是对自己的一种鼓励。

 ExChat

上半年还在继续开发 ExChat 项目,基本的群聊和私聊功能已经实现,但因为不擅长前端等各种原因,后来进展一直很慢,现在已经停止开发了。之后打算还是稍微维护一下,比如保持 Phoenix 版本更新,以及可以运行的状态,但目前没有增加新功能的计划了。除非有前端/客户端的同学感兴趣,让我只负责后端,倒是可以考虑重拾起来。

7 月份公司办了 Hack Week 活动,现在看来算是一个转折点,ExChat 从那之后就没再动过。当时我们用 Elixir 做后端,还拿了二等奖。

 grpc-elixir

从那之后,我开始想,要用 Elixir 做点什么。继续做 ExChat 还是再拿 Phoenix 做个什么应用? 结果是可能对 Phoenix 越来越了解,但一方面,我并不希望 Phoenix 变成 Elixir 的 Rails,另一方面,做这种项目,业务逻辑会占很多时间,以我当前的精力恐怕是很难在业余时间维护好的。那就造轮子吧,什么轮子呢?写个数据库啥的,以我的水平暂时怕是写不出来的,就整个简单的吧。碰巧 Hack Week 的项目需要用 gRPC,当时因为没有 Elixir 的库,只能勉强用 Ruby 搭了个 proxy,把一个 gRPC 服务转成了 HTTP。于是就想着要做个 gRPC 的 Elixir 实现,毕竟随着微服务越来越流行,加上 Google 的推广,gRPC 应该会被越来越多的使用,如果 Elixir 没有这样的轮子的话,岂不是很被动。

于是我剩下几个月就一直在折腾

Continue reading →


在 Elixir config 中使用 ENV 的一点技巧

看到了 Erlang Solution 的这篇文章,想到了这个话题。这篇文章虽然内容不多,但还是挺有用的。文中提到的编译时和运行时的区别,也是刚学 Elixir 搞不太清的问题。

而 Elixir 的 config 也并不是简单的启动前执行的代码,特别是在部署时。

有时我们可能会想在 config 里使用 ENV,比如:

config :my_app, api_key: System.get_env("API_KEY")

但直接这样写到 config 中是不行的。在部署时,比如通过 distillery 运行 mix release ,config 就会变成 sys.config,System.get_env(“API_KEY”) 已经被计算了,等运行的时候就不能动态得到 ENV 的实际值了。

有种做法就是像上边那篇文章中最后提到的方式,把 ENV 的获取逻辑放到函数里去做,这样就变成了运行时才会执行了:

# config.exs
config :my_app, api_key: {:env, "API_KEY"}

# my_app.ex
def api_key do
  get_env(Application.get_env(:my_app, :api_key))
end

def get_env({:env, key}), do: System.get_env(key)

Phoenix 的 config 支持从 ENV 中获取 port 就是这样处理的:

# https://github.com/phoenixframework/phoenix/blob/996a83a27d8ccdc7e0e3bdda9c21d537b19b2002/installer/templates/new/config/prod.exs#L15
config :<%= app_name %>, <%= app_module %>.Endpoint,
  http: [:inet6, port: {:system, "PORT"}]

 #

Continue reading →


写在流利说 Hack Week 之后

这周公司内部搞了为期一周的 Hackathon 活动,有十几支队伍参赛,很是热闹。

Hackathon 对我而言并不陌生,从大学起已经参加过三次 24 小时的,参与举办过一次 48 小时的。几乎每次参加 Hackathon 都会让我见识到很多牛人,也让我受到很大影响。

刚开始听说 Hackathon 就觉得很神奇、很让人热血沸腾,要在 24 小时完成构思、组队、开发、准备演示等全部的工作。粗想一下会觉得这怎么可能,但又让人按捺不住想去试一试的欲望。

第一次参加的景象还历历在目,它并不像现在很多的 Hackathon,有各种赞助商、高大上的场地,只是几个大学生发起的、面向大学生的,大概有二十个人参加,所有人都挤在一个可能还不到一百平米的住房里,待了一天时间,几乎都没怎么睡,就为做出一些东西。记得有几个师兄做了一个 web 上提交代码,然后返回编译结果的网站,对于当时基本不懂 web 开发的我,觉得这真是酷毙了。而我和另外一个朋友则用 C++ 写了一个 Windows 上类似泡泡堂的游戏。

除此之外,Hackathon 很有趣的另一点在于,因为很可能要和一些不认识的人组队,而大家的技术栈也不尽相同,所以经常需要快速学一个技术,然后马上做出点东西。虽然在时间本来就很紧张的几十个小时内还要去学个新技术,但因为目标明确,也倒挺有意思,而且经常有其他人带,所以很快就能上手。

从 Hackathon 也能看出技术的演进和趋势,开始是 Web,然后移动开发,现在则是各种硬件、人工智能,大家使用的编程语言也在不断变化。

公司的 Hack Week 跟以往的 Hackathon 差别其实很大。最明显的是时间长了,提前就可以想

Continue reading →


ActiveRecord 和 Ecto 的比较

ActiveRecord 是 Ruby on Rails 的 Model 层,是一个 ORM(Object-relational mapping)。Ecto 是 Elixir 实现的一个库,类似于 ORM。不管是不是 ORM,二者本质上都是在各自的语言层面,对于数据库操作提供了抽象,能让我们更方便地和数据库交互,而不是直接通过 SQL 的方式,并且对表中的数据做了映射,从而方便进行后续逻辑的处理。

这篇文章并不打算来争个孰优孰劣,很多时候对比的作用更是加深对于事物的认识。(当然不代表我本人没有倾向,只是希望大家能够尽量保持客观)

 定义映射关系

我们一般会以表为单位来操作数据库,二者也对表这个概念做了映射。假定我们有一个 users 的表,那么它们的定义(这篇文章的代码示范大多会使用这个模型)如下:

# ActiveRecord
class User < ActiveRecord::Base
end
# Ecto
defmodule User do
  use Ecto.Schema

  schema "users" do
    field :email, :string
  end
end

可以看到,ActiveRecord 更“智能”,通过继承了 ActiveRecord::Base 这个类,并且利用表名的转换约定(User 对应复数形式的表名 users)来完成映射。而 Ecto 中则是通过 DSL 定义了 schema,很显然,表名以及字段的指定都是显式指定的。

从代码上看,ActiveRecord 更简洁,但只看这个代码却不知道表名、字段等信息,需要通过查看 db schema 的定义来做进一步了解,而 Ecto 定义比较繁琐,但 schema 结构一目了然。

 数据存放和使用

让我们先跳过数据库操作,直接到数据被取出来之后的部分。我们来定义一个操作——把 email @ 前的部分提取出作为 name:

# ActiveRecord
class User < ActiveRecord::Base
  def name

Continue reading →


流利说为什么要从 Ruby 转到 Elixir

可能有些同学知道我最近都在搞 Elixir,基本除了工作时间,都在写 Elixir,不管是博客还是 GitHub,都是 Elixir 的痕迹。虽然写的挺愉快的,收获也很多,但还是有些遗憾的是,不能真正用到生产当中去。当一个工具没有办法真正在线上使用时,你永远也无法知道它的坑点与优点。也有很多人问,流利说用 Elixir 吗,我总是尴尬的说,目前还没有,但将来说不定会呢。

没想到,将来来的那么快。我作为公司内部比较早期深入接触 Elixir 的开发者,想来聊一聊为什么我们要从 Ruby 转到 Elixir,这并不是一时兴起做的决定,而是在长期使用 Ruby 的经验上,又加上对 Elixir 的研究和使用所做的决定。

提到这个话题,可能第一个想到的原因就是速度。因为众所周知,Ruby 很慢,而 Elixir 是基于 BEAM(Erlang 虚拟机)的,性能上要比 Ruby 快出不少。Erlang 基于 Actor 模型,虚拟机内部有自己的轻量级进程,都能够让并发编程变得很容易。而且对于分布式的架构,Erlang 也能够处理得很好。Erlang 的 OTP 也让我们能更好地做并发编程,并且帮我们处理 Erlang 进程的监控和重启等基本工作。

似乎 Ruby 圈子的开发者很多都觉得,性能嘛,没那么重要,以后优化就行。但我们现在觉得,性能对我们至关重要。很巧,Instagram 最近发的一篇博客,最后也提到说他们 performance 提高之后,用户数据也变好了。虽然我们现在的量级也许不是很大,但我们的野心还是很大的,所以性能问题对我们的影响力会越来越大,与其之后再去解决这个问题,不如现在开始做准备。另外,现在硬件发展的很快,几十个甚至上百个核的服务器都司空见惯,使用 Elixir 能够更好的利用这些机器,为我们节省更多成本。

虽然性能很重要,但是如果只是为了性能而换一个语言、一个技术栈,还是很难决定的一件事的。而且其他语言性能也很不错,比如 Go,但为什么偏偏选了 Elixir 呢?

可以说,几乎是必然的选择。

首先,两者的语法很像,这使得其他 Ruby

Continue reading →


Does 3% users matter?

好久没写博客,这几个月来的第一篇,居然不是技术的,实在是有愧于自己程序猿这个身份。无奈技术的博大精深,积累一篇高质量的文章实在是不容易,写的太烂也不敢发出来,怕被同行嗤笑。反倒因为不是做产品,才敢来聊一聊产品,虽然这雾中花,也不是我这俗人可以洞见的,但不论优劣,起码不至于会被骂得太惨。

这开头扯的差不多满意了,可以开始正文了。

早上开了一个会,提到了一个产品功能设计的一个依据是,3% 的用户如何如何,其它比例的如何如何。当时听了,觉得挺合理的,现在 PM 用数据把程序猿的嘴堵的严严的,有进步啊。但之后算了一下,按流利说的官方数字( http://liulishuo.com/about )——25,000,000 用户——来算的话,这 3% 有 750,000 个用户。

在这里,我意识到第一件事——思维惯性有多么可怕。3% 这个数字看起来不算很多,但当它放到不同上下文中去时,所代表的分量却不尽相同。即便是 1% 的比例,也可能影响着数以万计的用户。一个产品量级越大,相同的比例所影响的用户也就越多。当然,量级小的产品,比例的分量可能反而更重。所以比例这个数据能起到多少作用,真的是一个很模糊的问题。

随后,我又意识到另外一个问题——这个数据足以支撑一个决策吗?这些数据只是某几类用户的不同占比,但这些就足够了吗?如果这 3% 的用户日活都很不错呢?如果这 3% 的用户都是很优质的用户呢?印象中好像没有听到有关于这方面的数据,但如果假设成立的话,那是不是就意味着,这类用户反而需要我们投入更多的精力、花更多的心思呢?

这里引申出来的一个问题是,现在很流行 data-driven,但似乎并不是拿出了数据就可以用来 driven 了,起码得保证数据是可靠的、全面的吧。感觉很可能发生的事情是,有了一个想法,就想拿数据验证一下,然后也确实找了一些数据,也确实能够验证那个想法之后,就认为这是可行的,但有可能那些数据只是冰山一角。结果产品死了之后,还一定要说,我明明是 data-driven 的啊。就好像那句不知道是谁说的话一样,“人们只相信自己愿意相信的”。

Continue reading →


125 days contributing on GitHub

如果本文有个中文标题,大概会是这样:在 GitHub 上连续 125 天有 contribution 记录是怎样一种体验?

先来简单说说这 125 天里一些自己认为值得一提的事,可能这样大家才可能比较感兴趣地往下看。

  • 在 elixir-lang/ecto(类似于 ActiveRecord) 有 22 次提交,排名第8。
  • 完成了 Qiniu SDK for Elixir 的主要开发工作。
  • 其他零碎的,比如 elixir-lang/elixir、elixir-lang/plug、grpc/grpc 等等。

这 125 天大致可以分为三个阶段。

 开始

基本从年假前几天到年假结束,只是出于学习 Elixir 这门语言的原因,需要写点书上 demo 代码。根据我以往的习惯,会把这些代码扔到 GitHub 上,因为这样可能会增加一点 GitHub 的经验值?。当然,这次也是这么做了,不过坚持的还不错,整个年假每天都有看点书、写点代码。

年假过去后 Elixir 的基础学的差不多了,按照之前的习惯,GitHub 的 streak 就又要断了。不过幸好这次并没有,不然就不会有 125 天了。

 投入

学了一个东西,总要用一下的,不然只是看个书,copy 一些书上的代码,其实跟没学差不多。于是我开始想要做点什么呢,干脆做个网站吧,毕竟 Elixir 还是很适合做 web 的,而且又有 Phoenix 这个比较像 Rails 的框架。于是就花点时间做了一个到现在也没部署、没公开的小网站。这个网站其实不是重点,重点是,做的过程中,我发现 Elixir 生态系统还不太完善。比如,Ecto 有一些可以改进或者 fix 的地方,以及没有能够上传文件到 qiniu 的 Elixir lib。可能这些困难会导致放弃或者想办法绕过去,但我标榜自己是个 hacker 嘛,作为一个 hacker,是应该能够通过更优雅或者更出乎意料的方式解决这些问题的?。

于是我去给 Ecto 贡献代码。虽然我是 Elixir 新手,只能做一些比较相对简单的事情,不过还是学到很多 Elixir

Continue reading →


译:理解 Elixir 的宏-1

(原作者这一系列讲 Elixir macro 的文章写的非常好,我的认识还远远不够,所以在这里翻译一下,这是第一篇。翻译水平有限,见谅)

原文链接: http://www.theerlangelist.com/2014/06/understanding-elixir-macros-part-1.html

这是讨论宏(macro)的一系列文章的第一篇。我原本打算在之后要出版的 <Elixir in Action> 一书中来探讨这个问题,但又决定不这么做了。因为这个问题跟这本书的主题不太切合,这本书更关注于底层 VM 和 OTP 的关键部分。

于是我决定在这里探讨宏。个人而言,我觉得宏的问题很有意思,在这个系列里,我会试着解释它们是如何工作的,并对于如何写宏介绍一些基本的技巧和建议。尽管我确信写宏并不怎么难,但它确实需要一些比对一般 Elixir 代码,更高程度的认知,因此我认为了解 Elixir 编译器的一些内部细节是很有帮助的。知道幕后的事物是怎样运转的,能够让你对于元编程代码的思考更加容易。

这是一篇中等难度的文章,如果你对 Elixir 和 Erlang 比较熟悉,但对宏还是有一些疑惑,那么你找对地方了。如果你刚接触这两种语言,那最好还是先从其他的开始,比如 Getting started guide 或者干脆找本书来看看。

 元编程

很可能你已经对 Elixir 的元编程有一些熟悉了,基本上就是,我们有一些代码,这些代码能结合一些输入来生成其他代码。

借助宏,我们能够写出像 Plug 里一样的结构

get "/hello" do
  send_resp(conn, 200, "world")
end

match _ do
  send_resp(conn, 404, "oops")
end

或者这个 ExActor 里的例子

defmodule SumServer do
  use ExActor.GenServer

  defcall sum(x, y), do: reply(x+y)
end

Continue reading →


How does Plug.Builder work?

(本文涉及的源码需要有一些对macro的了解才比较容易看懂)

上回说到,我们可以利用Plug,搭配Cowboy这个web server来写一个简单的web app。而实际中我们不可能把所有的处理逻辑都放在一个Plug中,代码既不容易维护,也不能重复利用Plug。

正如Plug的名字(插头)一样,我们可以把一个个的Plug连起来,组成一个功能强大的Plug pipeline。就像你到国外旅行,电源适配器接口不对应,就可以买个转换器(其实就是一个插头),一头插着你的电源,另一头插在酒店的插座上。 再比如Plug实现了Plug.Logger,把它“插在”在你的整个Plug的pipeline中,就帮你加入了日志的功能。

Plug.Builder的目的就是为了让你方便地写出Plug pipeline,我们还是先来看文档里的例子? :D

defmodule MyApp do
  use Plug.Builder

  plug Plug.Logger
  plug :hello, upper: true

  def hello(conn, opts) do
    body = if opts[:upper], do: "WORLD", else: "world"
    send_resp(conn, 200, body)
  end
end

MyApp就是一个组合了几个Plug的Plug pipeline,按顺序有Plug.Loggerhellosend_resp是从Conn引入的,plug这个macro显然是从Plug.Builder引入的,那具体是如何引入,以及为什么这样就可以定义一个Plug pipeline,秘密都在use Plug.Builder这行代码中。

 Define plugs one by one

当以一个Module为参数调用了use这个macro(是的,use也是一个macro)时,其实最终只是调用了那个Module的__use__ macro而已。是的,还是macro ╮(╯▽╰)╭

我们来看Plug.Builder的__use_

Continue reading →


Should we do what users need or what we require them to need?

到底我们应该做用户需要的东西,还是要做我们要求用户需要的东西?

看这个句式,很显然,我的观点是后者。因为对于这个句式,作者自己的观点都是后边那个。

我们常常说,一个好的产品要满足用户的需求。这句话说的没错,一个连用户需求都满足不了的产品,不管做的好不好,一定活不下去,一个活不下去的产品也不能算是一个好的产品。所以你看小米,人家现在活得多滋润,就是因为人家抓住了用户的需求,并且极大地满足了用户需求,确实是一个好产品。

当然,我不是小米的粉,讲小米只是为了铺垫一下。(好像谁都看得出来)

如果目标只是定在了“好”产品这一级,那苹果现在也不会这么牛了。当年诺基亚也挺牛的,满世界都是它新品的广告牌,N 系列的拿出去,又实用,又漂亮,又能满足用户装逼的需求,当锤子使也挺好用的。而现在,不也就做着做着就做死了么。就是因为有一群坚信“用户根本不知道自己需要什么”的人觉得,用户要什么我就给什么,太没意思的,一点都满足不了我的控制欲,而且他们相信自己比用户有更高的品味、更好的审美,以及更加确定的相信什么才是更美的、人们更加需要的,他们不想只是遵守游戏规则,而是想要自己制定游戏规则。所以才有了进步。

本文的主角也不是苹果,苹果在这里也只是一个论据。另一个论据是 [stackoverflow.com](stackoverflow.com) ,还更像是本文的主角。

我自认为自己,像大多数自以为是的程序员一样,坚信自己有独特的品味、不凡的追求。写文字必须是 Markdown ,Word 是什么,好像没听过,可能是来自远古的东西吧。只用 Google,把 baidu 当做 ping 的web版,哪怕要“跨过山和大海”。看的书、用的系统、甚至出来“混”的名号,都必须是英文的,即便我们和外国人讲英文除了寒暄几句就不知道讲什么了。但当我从开始一点点用 stackoverflow 之后,还是有一种被雷击中的感觉。

stackoverflow 是一个技术的问答社区,里边有对于各种基本乃至更高级问题的各种答案,有谁说过离了它可能都没办法写代码了。在 stackoverflow

Continue reading →