绝对的说法都是错误的。

Rolling in the Deep (to Python)

时不时怀疑下人生也挺好的(⊙﹏⊙)b

Python 的优点就是用起来爽,开发效率高,语法简洁可以说是用起来爽的原因但不是开发效率高的原因,现如今用记事本、裸 Vi 做日常开发的应该是没有的吧?我认为开发效率高的几点:

  1. 动态语言特性。 基本上可以做到任何你想做的来减少重复工作(用 Golangreflect 你会默默留下眼泪),然后对数据的处理能力非常强大,当别人还在写迭代做类型转换的时候,你的数据已经处理完了..

  2. 完善的标准库和丰富的第三方库。标准库虽然有些比较鸡肋,但有总比没有好,第三方库的话,在 PyPI 上面搜下就知道了。

用起来爽也是原罪,你能感受得到在青轴键盘上运指如飞一天之后耳边回响着噼里啪啦的键盘声,手指酸爽的那个劲吗?

必须保证较高的测试覆盖率,Variable referenced before assignment/AttributeError/ImportError/Cannot use str as int 满天飞也不是不可能的,当然 pylint 可以帮忙做一部分工作。

面对计算密集型的任务你会想哭,当然还有 C 库可以帮忙,依赖 ctypes/ldconfig 又无法在源里直接安装的依赖很是麻烦..

多线程无法利用多核就得用多进程,这个可以说是致命缺陷!多进程之间通信、资源共享很是麻烦还不讨好,这大概就是有多线程的原因?

就上面这点说说工作中的情况:

  1. 在多进程里面做连接池共享连接是不可能的,在我们的配置下发和服务注册/发现的 SDK 中的解决方案是:只有一个进程使用连接从远端拉取数据,使用本地文件缓存,其它进程读缓存文件,这里拉取数据和写入数据的进程并不是独立的,为了保证接口的一致性,我借用了 tornado工厂方法模式,只不过不是通过平台的差异来生成不同的实例,而是通过竞争文件锁的方式来生成不同的实例,服务使用 gunicornpre-fork 模型,而gunicornmaster 会根据情况来重启 worker 来避免内存泄露,所以搞成这样有些情况又是不得不处理的,拉取和写入数据的进程挂掉了,其它进程必须补上来,这里对于读取者来说又要起一个单独的线程不断的检查是不是可以获取到文件锁,如果可以就可以假设写入者挂掉了,然后自己上位.. 当然也可能是锁文件被删掉了,所以写入者也得检测自己是不是还持有这把锁(⊙﹏⊙)b..

  2. 在近期的另一个 SDK 的开发中,内部服务就不说是干啥的了,拉取的数据“大无可大”,为了减少内存的使用率不让每个进程都将缓存文件加载到内存中来,我还给这货内置了基于 unix domain socketRPC .. 搞成这样又得处理更多情况,其实服务端那边是提供 HTTP 接口的,这里说下为啥不直接用一是怕服务受不了二是怕太慢,然后竟然不是基于 DNS 而是提供了一个 HTTP 接口来给出后端服务列表,我自己还得做一层简单的负载均衡容错封装,也提过为啥不使用 SOA(服务注册/发现 + RPC) 的方式(做点减法也何尝不好,至少有新的 feature 不需要又得推一次 SDK 的更新,不用处理用老 SDK 出的问题),恩.. 回到得处理的情况,因为数据文件比较大,在数据加载的时候(Python 啊,没错整个进程都会阻塞),和 unix domain socket 文件被删掉的时候,查询得回退到使用 HTTP 远端查询的方式,由于 Java 服务那边的各种不标准化,对查询结果的处理也让人心碎,然后还有写入者切换的情况,RPC 客户端又得重建连接..

以上,唯一让人欣慰的是即便搞了这么多 XXX 开发效率(进度)仍在 Java 之上,当然这也不好,快的一方会发现一些潜在的缺陷和设计不合理的地方,接下来得等待对方完善和修改对接协议(ˇˍˇ)

交织

猝…