闲置书籍出售

都是一些自己买来已经阅读过的书籍,放着也是占地方,不如转手给需要的人。

厦门市内自取,其他地方不包邮。

Read More …

hg 的配置

编辑 ~/.hgrc 文件

- 用户名、邮箱

- 开启 git 风格 diff

[diff]
git = True

- 插件

[extensions]
shelve=
mq=
graphlog=
rebase=
purge=
color=
prompt=~/.hg-extensions/hg-prompt/prompt.py

命令行信息提示

- 已经安装好 zsh 、 oh-my-zsh

- 开启 prompt 插件

$ hg clone https://bitbucket.org/sjl/hg-prompt ~/.hg-extensions/hg-prompt

~/.hgrc
prompt=~/.hg-extensions/hg-prompt/prompt.py

- 修改 oh-my-zsh 中 mercurial 插件配置

~/.oh-my-zsh/plugins/mercurial/mercurial.plugin.zsh

替换 hg_prompt_info 为下:

function hg_prompt_info {
    hg prompt --angle-brackets "\
<%{$fg_bold[blue]%}hg:(%{$fg_bold[yellow]%}><:><:>%{$fg_bold[blue]%})\
<%{$fg[green]%}<>@<>%{$fg[blue]%}>\
%{$fg[red]%}\
>%{$reset_color%}" 2>/dev/null
}

- 在 .zshrc 中开启 mercurial 插件

- 编辑主题文件,在需要提示信息的地方加入 $(hg_prompt_info)

hg

Gevent 手记

鼎鼎大名的 Gevent 。

协程核心思想是通过再次切分时间片,进行细粒度的上下文切换达到“并行”的效果,这种行为与线程类似,但是不在系统层面而是在应用的层面开销很小,所以称为协程。

- 所以某种程度上来说,协程的调度是应用主动进行的。

- 可以通过调用 gevent.sleep 来主动的进行协程调度

- gevent.select 也可以触发协程调度。

- 理想中的状态是,在发生 IO 和网络等待时触发协程调度。

- 相较于系统的线程调度,greenlet 的调度是可以预期的,但是这往往并不能代表结果就是可预期的,因为我们往往面对着随机的网络等待和IO等待。

- 可以使用 Greenlet.spawn 和 gevent.spawn 直接使用函数发起 greenlet ,使用 gevent.joinall 来同步协程的运行结果。

- 如果想用类的方式制作 greenlet 的执行体,需要继承 Greenlet 类,并且实现 _run 方法作为,执行的启动调用。

- Greenlet 的状态可以如下操作:

    started — Boolean, 协程是否启动
    ready() — Boolean, 协程是否被挂起
    successful() — Boolean, 协程是否执行完毕并且没有异常发生
    value — arbitrary, 协程的返回值
    exception — exception, 协程发生的异常,但是不会从协程中抛出到主逻辑进程中

- 使用 gevent.signal(signal.SIGQUIT, gevent.kill) 来时 SIGQUIT 信号与 gevent.kill 关联起来,防止僵尸协程的出现。

- gevent 中有很多种处理超时的方法。

import gevent
from gevent import Timeout

seconds = 10

timeout = Timeout(seconds)
timeout.start()

def wait():
  gevent.sleep(10)

try:
  gevent.spawn(wait).join()
  except Timeout:
  print('Could not complete')
    也可以使用with
import gevent
from gevent import Timeout

time_to_wait = 5 # seconds

class TooLong(Exception):
  pass

with Timeout(time_to_wait, TooLong):
  gevent.sleep(10)

- 猴子补丁

使用猴子补丁以后,gevent 替换了标准库的 socket 实现。

猴子补丁通过使用 gevent.monkey.patch_all 调用,也可以调用具体某个方面的补丁:

    gevent.monkey.patch_socket(dns=True, aggressive=True)
    gevent.monkey.patch_ssl()
    gevent.monkey.patch_os() # 替换了 fork
    gevent.monkey.patch_time() # 替换了 sleep
    gevent.monkey.patch_select(aggressive=True) # 替换了 select
    gevent.monkey.patch_thread(threading=True, _threading_local=True, Event=False)
    gevent.monkey.patch_subprocess()
    gevent.monkey.patch_sys(stdin=True, stdout=True, stderr=True)

- Gevent.event 相当于信号锁。event 的 set 方法可以通知所有的 wait 调用

- AsyncResult 可以在不同的协程中传递信息,可以有多个接收,(这点与 go 中的 channel 不同,channel 是有个数缓冲的, gevent 中类似广播机制)

- Gevent 中的 Queue 可以在不同协程中使用。

- 注意 Queue 中的 get put 与 get_nowait put_nowait 的区别

- 队列为空之后进行 get 将会阻塞,如果设置了超时,则超时后以后会抛出 Empty 异常

- 可以用 group 管理一组协程,方便 join 等操作

- group 也提供很多方法方便处理协程,如 map imap imap_unordered 等

- pool 与 group 作用类似,但是pool会限制并发数。

- gevent 提供了 local 作为保存协程内部变量使用,(效率更高?)不能跨协程使用。

- gevent 也提供 Subprocess 相关操作,可以直接用来替换标准库中的相关函数,这些都可以在协程环境下工作。

- 利用 gevent.socket.wait_read 与 wait_write 与标准库 multiprocessing 一起工作。

import gevent
from multiprocessing import Process, Pipe
from gevent.socket import wait_read, wait_write

# To Process
a, b = Pipe()

# From Process
c, d = Pipe()

def relay():
  for i in xrange(10):
  msg = b.recv()
  c.send(msg + " in " + str(i))

def put_msg():
  for i in xrange(10):
  wait_write(a.fileno())
  a.send('hi')

def get_msg():
  for i in xrange(10):
  wait_read(d.fileno())
  print(d.recv())

if __name__ == '__main__':
  proc = Process(target=relay)
  proc.start()

g1 = gevent.spawn(get_msg)
g2 = gevent.spawn(put_msg)
gevent.joinall([g1, g2], timeout=1)

- 利用 Gevent 实现 Actor 模式。

import gevent
from gevent.queue import Queue

class Actor(gevent.Greenlet):

def __init__(self):
  self.inbox = Queue()
  Greenlet.__init__(self)

def receive(self, message):
  """
  Define in your subclass.
  """
  raise NotImplemented()

def _run(self):
  self.running = True

while self.running:
  message = self.inbox.get()
  self.receive(message)
    使用
import gevent
from gevent.queue import Queue
from gevent import Greenlet

class Pinger(Actor):
  def receive(self, message):
    print(message)
    pong.inbox.put('ping')
    gevent.sleep(0)

class Ponger(Actor):
  def receive(self, message):
    print(message)
    ping.inbox.put('pong')
    gevent.sleep(0)

ping = Pinger()
pong = Ponger()

ping.start()
pong.start()

ping.inbox.put('start')
gevent.joinall([ping, pong])

Falcon 手记

Falcon

Python Web 框架。

主打速度、简单。

安装

如果不是在 pypy 环境下,最好先安装 cython 来加速。

pip install --upgrade cython falcon

如果是在 os x 环境下记得要设置编译参数。(很多东西的编译都需要这样设置,这是应为 clang 和 gcc 对于参数的处理差异产生的)

export CFLAGS=-Qunused-arguments
export CPPFLAGS=-Qunused-arguments

容器使用 gevent + gunicorn|uwsgi 的组合更能发挥最佳效能。

路由

falcon 使用方法命名的约定来做路由配置,

任何一个类,只实现了 on_get 方法,那么绑定路由到这个类,使用 GET 方法请求,就会被路由到 on_get 方法中进行处理。

钩子

与其他 Python wsgi 标准实现的框架一样, falcon 也是利用中间件思想来对整个系统进行解耦的。

在 falcon 中提供了两个钩子修饰器可以方便的对请求处理,

请求前
falcon.before
请求后
falcon.after

其他

faclon 对 req resp 也做了一些包装,还提供了一些辅助的方法,这些都是为了给开发者提供更大的便利。

总结

如果 flask 给自己的定位是 micro 框架,那么 faclon 就可以说是 micro micro 框架,除了最核心的路由控制进行比较个性的实现以外,再没有提供其他的东西了,也没有想 flask 那样的自由的组件机制,为其他东西留出位置。

总之,处理的绝对速度是很快的,但是其他的东西太简陋了,用来做 http 接口是合适的,用来做比较完善的网站应用来说就很不够用了。

但是话说回来,如果要做 http 接口,对比 openresty 也就没有任何优势了。

所以说 faclon 没什么用。

关于 Python 的误解

很多时候大家提到 Python ,附带的修饰词汇中总有“优雅”,Python 优雅吗?

在我看来这是关于 Python 的一个最大的误解,我个人的感受是 Python 一点都不优雅。

很多的时候写 Python 和写 PHP 感受类似,都是那种在泥坑里打滚的感受,当然你可以说使用 Python “可以”更优雅一些,或者说 Python 可有更大的优雅可能性,但是这并不表明 Python 就优雅起来了。

事实上,我们看到绝大多数的 Python 代码都不是那么优雅的, Python 是面向实用的语言,实用主义才是 Python 更本的哲学,而实用往往和优雅是背道而驰的。

Cocos2d-x 绑定脚本究竟选择 Lua 还是 JS ?

Cocos2d-x 绑定脚本究竟选择 Lua 还是 JS ?

这个问题感觉是有点可笑的存在,很大的程度上和 web 的大规模使用有关,当然直接关联的还是 HTML5 风暴。当你身边不从事 IT 的人都开始说 HTML5 的时候,你知道这个技术概念的副作用要来了。

好吧,闲话不说,我们说正题。

当下,在 Cocos2d-x 中,绑定脚本究竟选择 Lua 还是 JS ?

首先我们要明白,对比绑定脚本与对比语言本身是有巨大的差别的。简单的说作为绑定脚本的语言只是作为计算机语言本身的一小部分。

当我们通常在说起一种编程语言时,我们往往会隐含着很多有关那种语言的相关的东西。比如语言的演变历史,语言产生所要直面的问题,语言的生态环境,等等。

具体的说在 cc2dx 中,绑定脚本的作用就是调用框架本身的 cpp 的接口,这些接口以 cc2dx 框架本身的风格展现,所以,绑定脚本语言本身的特性被抹去了大半,而且因为 cc2dx 使用环境所限,绑定脚本也很难,依靠自己语言的各种生态环境中的资源,比如 Lua 没法直接用 luarock ,Lua 的 C 语言库也很难整合到项目中, JS 在这方面的处境就更尴尬了,因为 JS 设计之初就是为了让它运行在浏览器之中处理各种 web 问题, JS 的代码资源虽多,但是能直接向 cc2dx 提供帮助的却很少。

当然,还有一个问题我们需要明确,就是作为计算机语言本身, Lua 要比 JS 优秀,当然,我们对比两个事物的好坏的时候,需要有一个参照系,在这个语境下就是从作为计算机语言对于程序员的心智负担来讲的。

Lua 的设计从简,但是并不粗糙。 JS 设计确有很多让人用普通思维模式难以接受的地方。对于 JS 的语言设计问题的吐槽太多了,这里不赘述。单单是 this 作用域问题就让人如鲠在喉。

所以总结一下:

1. 对于绑定脚本而言,脚本语言本身的特性事实上已经被宿主框架限制,这个角度看,两种脚本语言没什么差别。

2. 在语言最基本的语法和一些实现特性上, Lua 比 JS 更加规范和更小的对于程序员的心智负担。

综上所述,当前, Lua 更适合作为 cc2dx 的绑定脚本语言。

开搞

一直都没有离开移动互联网,但是也一直没有参与具体的一线客户端开发。现在倒好,一下子 iOS 、 Android 同时开搞,惭愧以前看东西太过浅薄,书到用时方恨少。

不过也好,可以尝试一下极限学习的过程,也就是在未来的3个月要把 iOS 、 Android 开发都摸熟,能搞出东西,能把脑海中勾勒出来的东西做成具体的产品。

有段子讲:什么是人才?人才就是给你一个任务搞定了,再给你一个任务又搞定了。有时总抱怨时事不济,现在有了机会就是要去做个人才。

是为记。

使用 OpenResty 架设 Websocket Echo Server

喜大普奔! 春哥维护的 OpenResty 项目今天发布了 1.4.2.9 版本,最大的改进就是集成了 LuaRestyWebSocketLibrary ,这让开发 Websocket 服务器变得非常简单,这里我记录一下做一个简单的 echo server 。

我使用的系统是 ubuntu 13.04 64位,使用的 websocket 客户端是由 Python 编写的 websocket-client ( https://github.com/liris/websocket-client )。

首先下载 OpenResty 源代码、编译:

wget http://openresty.org/download/ngx_openresty-1.4.2.9.tar.gz

tar zxvf ngx_openresty-1.4.2.9.tar.gz

cd ngx_openresty-1.4.2.9

./configure --with-luajit

注意, OpenResty 依赖 perl 5.6.1+, libreadline, libpcre, libssl ,因为我已经安装过了,所这里没有安装步骤。如果没有安装可以执行 sudo apt-get install libreadline-dev libncurses5-dev libpcre3-dev libssl-dev perl make 进行安装。

$ make
$ sudo make install

这样 OpenResty 就被安装到默认的 /usr/local/openresty 目录中了,我们验证一下。

$ cd /usr/local/openresty/nginx/sbin
$ ./nginx -V
nginx version: ngx_openresty/1.4.2.9
built by gcc 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1) 
TLS SNI support enabled
configure arguments: --prefix=/usr/local/openresty/nginx --add-module=../ngx_devel_kit-0.2.19 --add-module=../echo-nginx-module-0.48 --add-module=../xss-nginx-module-0.03rc9 --add-module=../ngx_coolkit-0.2rc1 --add-module=../set-misc-nginx-module-0.22 --add-module=../form-input-nginx-module-0.07 --add-module=../encrypted-session-nginx-module-0.03 --add-module=../srcache-nginx-module-0.22 --add-module=../ngx_lua-0.9.0 --add-module=../headers-more-nginx-module-0.22 --add-module=../array-var-nginx-module-0.03rc1 --add-module=../memc-nginx-module-0.13 --add-module=../redis2-nginx-module-0.10 --add-module=../redis-nginx-module-0.3.6 --add-module=../auth-request-nginx-module-0.2 --add-module=../rds-json-nginx-module-0.12rc10 --add-module=../rds-csv-nginx-module-0.05rc2 --with-ld-opt=-Wl,-rpath,/usr/local/openresty/luajit/lib --with-http_ssl_module

然后我们编写一个需要使用的 nginx 配置文件:

#file: ~/Works/OpenResty/echo/nginx.conf

worker_processes  1;

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;

        location /s {
            content_by_lua '
            
            local server = require "resty.websocket.server"
            
            local wb, err = server:new{
              timeout = 5000,
              max_payload_len = 65535
            }
    
            if not wb then
              ngx.log(ngx.ERR, "failed to new websocket: ", err)
              return ngx.exit(444)
            end

            while true do
              local data, typ, err = wb:recv_frame()
              
              if wb.fatal then
                ngx.log(ngx.ERR, "failed to receive frame: ", err)
                return ngx.exit(444)
              end
              
              if typ == "close" then break
  
              elseif typ == "text" then
                local bytes, err = wb:send_text(data)
                if not bytes then
                  ngx.log(ngx.ERR, "failed to send text: ", err)
                  return ngx.exit(444)
                end
              end
              
            end
            wb:send_close()
    
            ';
        }
    }
}

然后启动加载写好的配置启动 OpenResty :

$ sudo /usr/local/openresty/nginx/sbin/nginx  -c ~/Works/OpenResty/echo/nginx.conf

然后我们用 Python 写个 client 测试一下:

#file: test_echo.py

from websocket import create_connection
ws = create_connection("ws://localhost/s")

print "Sending 'test echo'..."
ws.send("test echo")
print "Sent"
print "Reeiving..."
result =  ws.recv()
print "Received '%s'" % result
ws.close()

执行脚本

$ python test_echo.py
Sending 'test echo'...
Sent
Reeiving...
Received 'test echo'

测试成功!

这样,一个高性能的 websocket echo server 就搭建好了。