网络寻租

Programmer, Gamer, Hacker

是否要用装修公司

| Comments

最近忙装修,装修公司挑了无数家。在我们这种四线城市, 很多人不会去找装修公司,而是自己找装修队伍,我不是很认同这种方法, 诚然,自己找装修队可以节省好几万的费用,但是钱不是这样省的。

装修过程有几个方面:

开始的格局设计:考虑什么房间做什么事情,里面的家具如何放置,动线,通风,采光什么的。 没有在这个阶段深入思考,装修出来发现问题再改就来不及了。某朋友自己装修, 完了才发现一个房间空调没有地方放,再打洞就会毁坏掉房间的装潢,很麻烦。 我们购买的是复式楼,在找设计公司的时候,群策群力,分别提出了各种的方案,比如考虑楼梯的位置,就有三种选择。 每种选择都有不同的功能区域划分,然后功能区还需要细化。如果不找装修公司,自己是想不到这么多方案的。

硬装:包括水电,木工泥工什么的。工程方面有很多很多的门道,自己可以去研究,弄清每道工艺, 但是再怎么研究,也比不上有经验的施工人员。施工的过程中也会出现一些突发状况, 需要有施工经验的人帮助参考。某朋友自己找施工队,楼顶敲出来三处,结果防水没有做好,返工了好几次, 损失的钱都可以找装修公司了。

软装:我们去过一些朋友家,自己装修和找人装修的效果区别巨大,很明显就认出谁的家是设计过的。 装修很花钱,效果好不好却很大程度体现在软装上面,一般人对空间色彩不够敏感,也没有很多的设计可以选择。自己装修,花大笔钱,最后却没有一个好的样子。 除非自己是设计领域的,软装最好还是找人把关,自己乱买东西最后搭出来一个古怪的风格。

总而言之,专业的事情交给专业的人员。不要什么事情都自己做。 大脑容量是有限的,多充实自己的专业知识赚更多的钱购买服务更划算。

至于行业鱼龙混杂,找不到好的装修公司,或者找到装修公司乱开价的问题,就是“战术”层面的问题了。

我的看法是:根据开销,选择对应档次的装修公司。普通人也不需要顶级的设计师和装修公司, 我觉得满足要求的就可以了(当然满足基本要求也需要挑好久)。最好是原先有经验的设计师出来单干开新公司,价格上优惠,人员也是有经验的。

至于装修公司乱开价问题,这种非标准化服务,没有办法横向比较,只能自己多研究,可以找一些连锁的公司,管理规范一些。 收费方面每个项目自己都要弄明白,这样才不容易被蒙骗。装修不能做甩手掌柜。

另外装修公司也是希望把项目做满意的,项目满意名气就更大,名气大了来的人多,收费也可以更高。 走上合作的道路对双方都有好处。

在项目过程中,顺利的沟通非常重要。 设计师能够提供专业领域的把关,但是生活方式和房子的风格还是只有自己清楚, 需要让设计师弄清楚自己的需求,而不是顺着设计师的思路,弄出一套样板房,自己住着不舒服。

更好的命令行

| Comments

命令行大家应该都熟悉了,但是命令行其实是很烂的, 输入和输出都采用字符串的方式,而不是一个规整的数据结构。 这样的后果是,每个程序都需要自己解析输入,以及提供一个特殊结构的输出, 很容易出现解析上面的问题。

从本质上面说,命令行就是一个函数执行的接口,终端就是一个状态机, 每一个命令就是一个函数。那么为什么不像通用的编程语言的函数一样来定义?

比如这样来写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 普通操作
$> date
=> "Sat May 23 09:18:38 CST 2015"
$> rm('/fdas/fdas/fda', directory: true, force: true)
=> true

# 输出结构化
$> ls
=> [{mode: 'drwxr-xr-x', user: 'halida', group: 'halida', size: 123, name: 'Gemfile.lock'}]
$> ls(columns: [:name, :size])
=> {name: 'Gemfile.lock', size: 123}

# 管道
$> history >> select{|row| row[:cmd] =~ /mina/ }
=> [cmd: {'mina init'}, run_at: '2014-02-01 10:00:21']
$> ls >> filter(:name)
=> ['Gemfile.lock']

# 很多命令应该用面向对象的方式来执行,更容易理解
$> master = git-get-commit('master')
$> master.files.count
=>3
$> master.committer
{name: 'James', email: 'james@gmail.com'}

# 输出日志就算是以文字的方式体现,也最好结构化起来,方便解析
$> log = log("Process file (filename) finished, spend time (spendtime)", filename: "xxx.mov", spendtime: 220.seconds)
$> log.spendtime
=> 220.seconds

网上搜索了一下,看起来没有什么好的解决方案,有时间我可以开发一个看看。

Rails导出数据经验整理

| Comments

最近做了一些rails数据导出的工作,就是把一些特定的ActiveRecord数据挖出来,保存到表格里面。 需要注意几件事:执行速度,内存消耗,以及调试速度。

执行速度

导数据的程序基本上就是一个循环体,外部获取数据集,内部把一条数据转换成表格。 在内部,往往需要通过一条记录作为主体,通过数据库逻辑关系顺藤摸瓜挖出一批数据, 这样会形成一批短查询,因为是在循环体里面,会带来很大时间上的消耗,比如:

1
2
3
4
out = []
Unit.where("active_at" < Time.now).each do |unit|
  out << [unit.company.name]
end

可以先把这些查询汇总起来,一次查询掉,然后在循环体内部筛选出对应的数据:

1
2
3
4
5
6
out = []
units = Unit.where("active_at" < Time.now)
company_names = Company.where(id: units.pluck(:company_id).uniq).pluck(:id, :name).to_h
units.each do |unit|
  out << [company_names[unit.company_id]]
end

内存消耗

查询大量数据的时候,可以首先查所有的ID,然后分批查询,这样防止序列化大量的数据库对象:

1
2
3
4
5
6
7
unit_ids = Unit.where("active_at" < Time.now).pluck(:id)
group_size = 100
unit_ids.in_groups_of(group_size, false) do |ids|
  Unit.where(id: ids).each do |unit|
    ...
  end
end

数据全部缓存在一个array中的话,会占用大量内存,最好是通过数据流的方式一个个输出处理,用后即丢:

1
2
3
4
5
6
7
8
9
def export
  Unit.where("active_at" < Time.now).each do |unit|
    yield([unit.name])
  end
end

CSV.open('out.csv', wb) do |csv|
  export { |row| csv << row }
end

在循环体内部,尽量用局部变量,不用的资源会更早释放。

跑数据导出的时候,最好同时注意一下服务器的剩余内存。不要把其它服务搞挂了。

调试速度

导数据最花费时间的往往还是调试过程。

调试的时候,可以只返回几条数据,检查完毕之后再全部跑。

1
2
3
4
5
6
7
debug = true
index = 0
Unit.where("active_at" < Time.now).each do |unit|
  yield([unit.name])
  index += 1
  break if debug and index >= 10
end

需要考虑数据并不是很规整,做好预防性编程。

1
2
3
Unit.where("active_at" < Time.now).each do |unit|
  yield([unit.company.try(:name)]) # 公司可能不存在
end

很多时候难免特定数据不符合预设状况,最好循环体内部记录log,出现问题可以跟踪。 比如下面的例子,unit没有oldest_driver的时候,会报错,记录了日志,就知道在哪条上面出现了问题。

1
2
3
4
5
6
7
logs = []
Unit.where("active_at" < Time.now).each do |unit|
  logs << "unit: #{unit.id}"
  oldest_driver = unit.drivers.order('age desc').first
  # 
  yield([ordest_driver.name])
end

导出数据可以生成两个版本,一个给客户,另外一个加上一些debug数据方便自己分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def export
  Unit.where("active_at" < Time.now).each do |unit|
    yield([unit.name, 'debug', unit.id])
  end
end

CSV.open('out.csv', wb) do |csv|
  CSV.open('out_debug.csv', wb) do |debug_csv|
    export { |row|
      debug_csv << row
      csv << row[1..1]
    }
  end
end

架构设计

代码架构上面最重要的是职责清晰。导出数据的逻辑比较简单,分离出导数据类,以及处理数据类就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module Exporter
  def iterator_to_csv(filename, iterator)
    CSV.open(filename, wb) do |csv|
    iterator.call { |row| csv << row }
  end
end

class ExportActiveUnits
  attr_accessor :time
  def export
    Unit.where("active_at" < time).each do |unit|
      yield([unit.name])
    end
  end
end

eau = ExportActiveUnits.new
eau.time = Time.now
Exporter.iterator_to_csv "out.csv", eau.method(:export)

学习ASP.NET心得

| Comments

因为现在公司的一个网站项目是用ASP.NET写的,我周末抽时间学习了一下相关的概念,对于ASP.NET有了一定的了解。 学习过程就是看一遍W3School的教程。 我没有下载Visual Studio实际编写,因为只需要能够看懂项目代码就可以了。

总体来说,我觉得微软的技术有这些特点:

复杂:微软为了让普通程序员能够上手,做了大量的工作,集成在Visual Studio里面。 你用微软的集成环境,按照教程一步步做,就可以跑出来一个东西。但是在这个过程中,你会发现接触到一堆微软特有的概念,以及用到大量微软设计出来的工具。 可能有的人认为微软的东西比较简单,刚刚毕业的大学生就可以拖拉出来一个东西。但是仔细看看,就会发现微软的东西非常复杂,每一步都隐含了大量的复杂度。 微软有自己的设计思路和概念,但是感觉挺别扭,要么是离本质有点远,要么是文档讲了一堆,你还是搞不清楚它到底是什么。

既定路线:如果按照微软设计的路线走,可以很快开发出来一个东西,但是如果需要定制,就会面对重重阻力:工具和库假设你会按照这条路走。 如果说这条路线很顺也就罢了,但是我感觉微软的设计总是不够好,感觉有些偏门,不是“大道”。

依赖:学习一个东西,牵连出来需要学习一整块的东西,而不是单独学习一个内容。同时用了微软的一个工具,就要用微软的一套工具。 这是微软的赚钱战略,如果只生存在这个环境里面就没有什么问题,但是眼界宽一些,感到被限制住就有点难受了。

学习曲线:使用微软提供的解决方案,能够很快进入状态,写一个东西出来,比单独组合工具学习曲线低。 但是再更深一步就很困难,学习曲线就陡峭起来。这里面就有一个平衡:选择更强大的工具,还是更容易理解的工具。 更强大的工具能够更快,但是带来了更多的复杂度,复杂度过高,人就无法控制了。

现在用微软解决方案的人越来越少,这是好事。作为程序员其实自主选择容易被大环境控制,其他人如果都用一个烂工具,很难独善其身。 你不可能采用一个很少有其他人会用的技术,一个是技术成熟度需要程序员的时间去堆,另外一个是项目不是一个人的事情,要有其他人一起做,或者至少能够找到维护人员。

微软的操作系统也是,国内基于微软的生态系统乱七八糟,作为程序员自己可以用linux或者apple的生态圈,但是总是免不了被其他人拉回来, 重装系统,解决问题。看到操作系统差劲的用户体验,漫天飞的木马病毒,大大小小的流氓软件,死活找不到东西的搜索引擎,不由觉得普通人真辛苦。

用chef对少量服务器进行配置管理

| Comments

原先介绍了chef,现在需要面对一个实际的问题:如何用chef管理少量的服务器。

我希望:

  • 能够对几台或者十几台服务器进行配置管理。
  • 针对每台服务器,写yml格式的配置文件,执行一个命令之后,就可以配置好这台服务器,同时源码管控这个yml文件的变更。
  • 支持复杂的服务器配置,包括启动项目管理,自动告警,日志归总等。
  • 不需要管理服务器,比如chef-server这样的东西,只需要留有本地的配置文件。

我采用的解决方案:

  • littlechef,这个项目可以把chef-solo,一个本地跑chef的方法,部署到远端服务器上面去,同时拷贝本地的recipe和配置文件到远端,执行需要的操作。
  • 写recipe,让部署能够通过写node配置文件进行配置,比如启动服务,日志归总,服务器管理员用户,自动重启更新等。
  • 用yml格式撰写node配置文件,以及datatag配置文件,然后用自动化脚本转换成json格式。手动写json太反人类了。
  • 远端的脚本用ruby写,脚本里面的参数不是用erb渲染出来的,而是把配置序列化成yml,ruby脚本再读取它们。这样远端服务器上面的执行代码是规整的,人可以阅读。

都弄好之后,可以写这样的服务器配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
---
  # 服务器名字
  name: "test"
  # 基础配置的recipe
  base-system:
    # 是否跑软件包更新
    update: false
    # 是否设置自动更新
    auto-upgrade: true

    # base-system会自动创建一个deployer管理员用户,谁可以通过公钥的方式访问这个用户
    authorized_keys:
      - "linjunhalida"

    # 写在databags里面的配置参数,可以连接到slack发提醒
    notify: 'test'
    # 是否每天判断服务器是否需要重启或者更新
    check:
      need_reboot: true
      need_upgrade: true
    # 设置服务器定期重启
    schedule-reboot:
      minute: '26'
      hour: '8'
      weekday: '2'
    # 设置服务器自动升级安全更新
    schedule-upgrade:
      reboot: true
      minute: '1'
      hour: '0'
      weekday: '1'
    # 服务器启动的时候跑的应用
    start:
      - name: "railsapp"
        user: 'deployer'
        pwd: '/home/deployer/apps/railsapp/current'
        cmds:
          - 'bundle exec thin restart -C config/thin.yml'
          - 'RAILS_ENV=staging bundle exec rake resque:restart_workers'
          - 'RAILS_ENV=staging bundle exec rake ts:restart'

  run_list:
    - "recipe[chef-solo-search]" # so chef-solo search can work
    - "recipe[base-system]"

chef部署之后,会创建目录/etc/base-system,里面存放各种配置和执行脚本,同时安装到crontab,/etc/rc.local等各种地方。 服务器的关键操作,比如重启,安全更新结果,都会通过notify功能汇报到slack上面。 我可以通过node配置文件,清晰看到每台服务器是如何配置的。并且这个系统可以演化,更换一种配置方式,只需要重新跑一下部署。

不过还是有一些难办的问题:

  • littlechef项目成熟度不高,使用起来不是很舒服。
  • 架构复杂:chef已经很复杂了,远端还要部署一个ruby环境,架构复杂带来调试和理解上面的难度。
  • 学习成本高:维护者需要弄懂chef,远端编织起来的ruby框架,之后才能配置服务器,最后可能只是需要加上一个小东西。
  • 维护成本:又多了一个项目了。

正确使用社交网络的方法

| Comments

我们常用的社交网络有:微信,微博,QQ,豆瓣,它们消耗了我们大量的时间,其中绝大部分都被浪费了。

我们在无聊的时候会拿出手机,刷一下动态。刷动态的时候,大脑通过接受信息,获得了奖励:感觉到和他人建立了联系,学到了新的知识,享受到了快感。 这个过程强化了神经反射:我们有了冲动,经常拿出手机来刷一下,期望能够获得奖励。这是上瘾症状。

我们需要反省一下,应该如何更好地使用社交网络。它对我们的用途有:

  • 娱乐:从其他人分享的内容上面取乐。
  • 关注认识的人:获取动态,能够保持联系。
  • 关注有意思的人:获取资讯和洞见。

在满足这些用途的前提下,我期望能够更少消耗时间,更好和他人保持联系,获得真正重要的信息。所以这里列了一下更好的使用方式:

  • 社交网络核心的用法是记录人:记录认识的人的联系方式,收集有趣的人的讯息。
  • 我不建议把社交网络作为娱乐方式,因为:娱乐正是给我们大脑奖励的主要手段,娱乐性内容是成瘾的主要一环,娱乐要么正正经经地玩个痛快,而不是一点点的享受,欲求不满容易刺激上瘾。
  • 和他人的沟通和联系,尽量用语音聊天或者见面聊,时间成本更低,收益更高。
  • 保持联系和关注认识的人:每天最多过一遍认识人的动态,尽量回复,而不是点赞,这样他人对你印象更深刻。觉得无关注价值的人,就不用看他的资讯了,记录联系方式即可。
  • 获得洞见:大多数的信息是碎片化的,不成体系没有阅读价值,建议通过看书获得。最新资讯可以关注一些高质量的讯息来源。
  • 只利用碎片时间查看和处理动态:没其他事情可做,以及有明确截止时间。比如:等车,等饭,上厕所的时间。整块的时间里,千万不要打开这些手机应用。

Rails容灾恢复策略

| Comments

我们可以把一个运行的rails环境,根据处置的方式,拆分成以下几个部分:

  • 系统环境:跑rails必须的服务器系统环境,比如特定版本的ruby,各种第三方库,第三方工具等。
  • 项目配置:和当前运行环境相关的变量,比如数据库帐号,各种第三方API和服务的连接方式。
  • 项目代码:除去和运行环境相关的rails应用代码。
  • 应用数据:比如数据库,本地一些需要持久化的文件等。

拆分的原因是因为以上几个部分有不同的特性,在构建,备份,恢复的过程中需要用不同的方式来对待。 特性包括是否经常发生变化,变化的频率,是否和运行环境相关等。

首先看构建过程。构建过程往往非常繁琐,需要能够自动化进行。拆分成以上几个部分后,构建方案如下:

  • chef构建系统环境。
  • cap把项目配置和项目代码构建出应用的执行环境。
  • backup Gem备份和恢复应用数据。

发布新版本,因为只改动了项目代码,只需要重新用cap重新构建执行环境。 需要增加配置,或者修改应用数据,也不复杂,执行一些特定脚本即可。

系统恢复:可以重头构建,但是时间消耗太长,更好的方案是针对服务器做镜像,也可以用docker做应用级别的镜像。 另外也需要备份好项目配置,以及隔离和备份出来应用数据。 出现问题的时候从镜像创建环境,修改配置,恢复备份的应用数据,然后发布最新的代码。

Rails项目的发布

| Comments

capistrano/mina可以用来发布rails项目,它们是应用发布的最佳实践。

关于应用发布,需要满足以下要求:

  • 发布的版本确保可以运行之后,才替代现有版本。
  • 留存有旧的版本,必要的时候可以恢复。
  • 发布版本不应该带有版本管控信息,防止不必要的信息泄漏。
  • 配置文件,数据文件和代码分离:另外存放,不和发布的代码混在一起。

capistrano/mina的解决方案:

  • 发布的各个项目版本放在releases目录下面,各个版本的目录名称按照1,2,3的顺序递增。
  • current是真正跑的版本,是指向release的软链接,当新项目发布成功的时候,再修改软链接。
  • scm目录存放带有项目管控的代码。
  • shared目录存放配置文件,数据文件,按照需要软链接到各个发布版本里面去。

配置:

capistrano(以下简称cap)写一个config/deploy.rb,里面定义了一系列的rake任务,以及一系列的role(角色,比如数据库,应用服务器,网站服务器), rake任务定义了在什么role上面执行什么命令。各种配置环境写在config/deploy/文件夹里面,命名producton, staging等, 当需要发布项目的时候,执行cap production deploy,就根据配置环境和deploy脚本执行操作。

执行任务过程如下:

  • 创建目标环境releases/n
  • scm获取最新的项目,然后根据配置中指定的版本号,拷贝代码到releases/n
  • 初始化releases/n
  • 把app/release/23链接到app/current,然后重启服务
  • 清理releases/目录,只保留最新的几个版本

cap在服务器上面执行代码的方式,是通过维护一个ssh连接实现的,每次执行任务都要上传命令,返回结果,如果ssh连接比较慢的话,整体消耗时间就很长。 mina它的原理是生成一个bash脚本,上传到服务器上面执行,这样执行效率比cap高太多,大家可以考虑作为替代使用。

这种方式是传统的编译发布,另外有直接发布环境的方法,比如用docker。不过这种发布方式我没有研究清楚,等研究过之后再比较吧。

Slack介绍-企业级实时通讯工具

| Comments

最近我在团队里面推广了slack,它是一个文字实时通讯工具,为什么要用它呢? 比起QQ,skype等通用实时文字通讯工具,有几个好处:

  • 文字交流内容可以在团队之间分享。
  • 交流按照channel主题分组,比如项目1,服务器管理,客服等。
  • 可以存档,以及搜索历史记录。
  • 安装了手机应用或者桌面应用了之后,可以实时收到提醒。
  • 可以和其他各种系统整合,比如redmine,gitlab,或者程序员撰写提醒插件(比如监控服务器性能,登录,应用发布,定期任务等)
  • 聊天机器人:机器人可以通过监控聊天室的内容,执行特定操作:
    • 这样团队成员可以控制机器人做一些事情(查看系统状态,执行特定任务等),这些控制指令可以在手机上面发出。
    • 机器人监控各种提醒,执行操作。

因为这些好处,我觉得slack这种工具值得所有的技术团队使用。

不过同时slack也有一些其他问题:

  • 聊天分组是按照channel来的,不能进一步区分,比如服务器管理下面还有系统性能,系统重启,系统登录等子分组。这样用户可以关注特定的子分组,而不会被大分组的其他内容刷屏。
  • 通讯不够实时,有的时候可能好几分钟才能响应,一些紧急时间几分钟是很长的时间了。

比特币介绍

| Comments

大家应该对比特币不陌生了吧,不过弄懂比特币原理的人应该不是很多。 这里有一篇中文介绍,讲得非常清楚。

主要的原理:

  • 比特币简称BTC,一个比特币可以拆分到10的-8次方。
  • 用户拥有钱包,钱包由公钥私钥组成。这样用户可以给交易做签名。
  • 比特币网络维护一个全局的账本,账本里面记录的是交易:一个用户转多少BTC给另外一个用户,所以BTC本质上是通过账本计算得到一个用户到底有多少钱。
  • 一次交易就是交易双方广播一条交易记录到比特币网络里面,被大多数人接受了,那么这个交易就成立了。

那么比特币如何产生?交易需要写入全局账本才算正式成立,全局账本拆分成一个个block,任何人都可以收集交易,创建block。每创建一个block,创建者可以在里面记录自己获得若干BTC,这是比特币产生的唯一途径。比特币网络每个小时只能接受若干数量的block,这样保证比特币产生的数量稳定。创建block的人要互相PK,谁运算量更大就更有可能被接受。创建block的过程叫做挖矿,挖矿可以获得比特币,同时也为大家服务:创建新的账本。

比特币协议规定,随着时间的增长,每个block获得的BTC会变少,总体来说,比特币是通货紧缩的(如果更多交易用到比特币的话)。开始的人更容易获得比特币,大家更愿意把比特币囤积起来一段时间。同时币值完全取决于市场,大家对比特币多有信心,它就值多少钱。比特币的用户数量,真实交易频率可以作为它生命力的象征。

我觉得比特币的实际用处在于交易本身。因为比特币是去中心化,以及不需要用户信息的,可以利用它进行匿名跨国交易,比如快速从中国转钱到国外或者相反,以及各种黑市交易。有了比特币,资金交易可以真正做到匿名安全。从这个角度看,它是相当有价值的。

因为账本是全局的,我们可以看到有史以来所有的比特币交易记录,比如这一条,里面列出来创建者获得25BTC,以及各种交易记录。我看了一下最开始的记录,应该是比特币的发明者自己留的,他留了至少100万个比特币,按照现在的币值,价值2亿美元。

初始创建者获利甚多,于是就有无数的山寨币出来。但是同时货币的去中心化,匿名特性,让它变得无保障,经常会有黑客盗窃,网上交易所诈骗等现象,用户同时也没有办法维权。加上币值容易被操作,个人觉得比特币不适合持有,而是用来进行货币交易转换。