Jul
14
2010

我常用的Android应用程序

入手HTC Hero有大半年了, 说来这也应该是版本更新最缓慢的一款Android手机了. 不过还好有大神自制ROM, 所以基本上在第一时间就能用上最新的版本. 最近更新Android后, 我没有备份应用程序, 打算想到什么的时候再装上. 经过一段时间后, 可以出个常用应用程序的列表了.

必须要的应用

  • Gmail: 这个没舍好说的; 必须要
  • Twitter for Android: 虽然在大陆被封, 但我实在离不了. 因此有了修改版
  • NewsRob: 个人觉得比较好用的Google Reader Android客户端, 和Twitter一起, 是掌握信息的主要来源, 必须的.
  • Wireless Tether for Root Users: Android 2.2 Froyo号称是支持原生的Wi-Fi Hotspot的, 但是有硬件限制. 比如HTC Hero就悲剧的不支持. 作为替代, 就用这个了, 前提是你要有Root权限.
  • NetCounter: 用来统计手机网络(Edge, GPRS, 3G)使用的流量和Wi-Fi使用的流量. 主要用到前者, 中国移动和流量太贵, 要省着用. 希望有没有必要用这个应用的一天.

实用的应用

  • 支付宝: 恩…用来冲话费挺方便, 我也只用过这功能….
  • Google Maps: 大致知道自己在哪里, 有时候也查查公交线路舍的, 不过并不是总是靠谱.
  • Google Talk: 没舍好说的…
  • Compass: 指南针. 暂时还没用到. 有备无患.
  • 中华万年历: 有各种农历的节气, 传统节日, 还有黄历等功能, 比较喜欢.
  • Advanced Task Killer : 有人说, Android系统总是要用此类杀进程的软件杀进程才能跑的比较快. 对此, 我倒并不认同. 个人不觉得应用开多开少在大多数情况下有多大影响. 当然有些程序自己有问题, 那倒是的确会卡住. 我装这个应用的目的也主要是这个, 杀那些不重启就工作不正常的应用.
  • ConnectBot: 偶尔情况下能够用到. 比如我有几次用Cider跑游戏的时候退不出来时, 用它连接到Mac杀进程, Reboot…
  • setCPU: 额…超频用的. 能设置多个Profile. 比如用电池的时候就用这个频率, 不用电池的时候就超个频率, 跑的欢点.

一些Widgets

听名字就知道是干嘛的, 比较符合个人审美观的几个:

  • Retro Clock Widget;
  • AutoRotate OnOff;
  • BatteryLife;

最后

可见我还是比较把手机当手机用的人. 用的应用程序相对来说很少了, 手机自带的耳机也还没开封呢…..另外求推荐好玩的应用.

Jun
21
2010

Twitter for Android for Mainland China (修改,自定义API方法)

由于众所周知的原因, Twitter for Android在中国大陆是无法正常工作的. Android上其他一些Twitter客户端比如Twidroid可以自定义API, 如果Twitter for Android也可以, 自然就没有烦恼了. 不过从最近Twitter官方的各种表现来看, 不要指望他们会在自己的Twitter for Android里加上自定义API的功能.

如果不是对Twitter for Android的界面情有独钟, 我也不至于折腾这玩意, 基本属于浪费时间, 其他客户端也用的好好的.

准备工作:

  • com.twitter.android.apk, 菜市场里下载, 然后用adb或者其他方法搞到SD卡里去, 再整到电脑里.
  • smali, An assembler/disassembler for Android’s dex format, 用来解开Android的dex, 修改完后再组装起来. 两个jar包, baksmali.jar和smali.jar. 下载地址: http://code.google.com/p/smali/downloads/list
  • Auto-Sign, 用来签名Android App的. xda和一些Android开发论坛上有, 懒的找的就点这里下载.

开始修改:
1, 解压com.twitter.android.apk, 这玩意其实就是一zip包.

unzip com.twitter.android.apk

或者用各种图形界面解压工具解压之.

2, 反编译(?, 我不知道这是不是反编译.) classes.dex.

Java -jar baksmali.jar classes.dex

会得到一个out目录.

3, 替换其中的字符串常量twitter.com为自定义的API. out目录里全是smali结尾的文件, 所以

find out -name “*smali” | xargs sed -i ‘s/twitter\.com/xxx.xxx.xxx/g’

Windows用户就自己找个顺手的编辑器, 查找替换吧.
要注意的是Twitter的搜索API是search.twitter.com, 自定义API后则是search.xxx.xxx.xxx; List的API是api.twitter.com/xxx, 自定义API后则是api.xxx.xxx.xxx/xxx. 如果用自定义的API Proxy, 大多要把search和api去掉才是正确的json地址. 这点可以直接把URL放到浏览器里试试.

4, 把out目录重新打包成classes.dex. java -jar smali.jar out/. 会生成一个out.dex, 替换掉原来的classes.dex. 删除原来的META-INFO签名文件. 把AndroidManifest.xml文件, res文件, classes.dex文件, resources.arsc文件打成zip包. (用你顺手的方法)

zip -r unsigned.apk AndroidManifest.xml res classes.dex resources.arsc

5, 用下载的Auto-Sign签名程序.

java -jar signapk.jar testkey.x509.pem testkey.pk8 unsigned.apk signed.apk

unsigned.apk就是上一步里打包的未签名的程序包, signed.apk就是已经签好名的. 当然名字无所谓, 可以随便改.

修改完毕.

附上一个我修改好的: Twitter for Android

May
13
2010

Macbook Pro更换硬盘数据备份及注意事项.

因为硬盘告急, 另外又想在Macbook上折腾下Linux, 只好买了一块新硬盘.

在更换硬盘前, 首先做好数据备份. 首选Time Machine. 如果本来就是Time Machine的备份, 直接换上硬盘就成了. 如果没有多余外接硬盘, 我的做法是, 新买来硬盘分出两个区来, 一个用来Time Machine备份, 一个用来做系统. 这个Time Machine的分区其实也只是为了方便重装.

接下来是考验动手能力了. 我的Macbook Pro型号是2009年中的13寸, 点这里下载Apple的帮助.(也可以自己去Apple Support搜索, 如果你不是我这个型号的话, 当然是也可以去搜..= =) . 需要的工具有, 十字的小螺丝刀, 用于拧下Macbook Pro后盖的螺丝. 六角型的螺丝刀, 用于拧下Macbook Pro内置硬盘边缘用于固定的螺丝. 我这两个一样都没, 结果从家门口的一个修手机的师傅那借来的…悲剧的是, 一开始并不知道内置硬盘边缘的六角型螺丝有用, 直接把买来的硬盘放了进去. 后来咣当咣当响了, 才知道硬盘没有固定好, 重新打开, 用买来的硬盘自带的两个螺丝勉强固定了下. 又后来把内置硬盘塞硬盘盒塞不进去时才发现…原来这几个六角螺丝是可以拿下来, 换到新硬盘的. 不好意思再麻烦别人就继续这么撑着, 这周末去买副螺丝刀吧…

硬盘放进去后, 就可以恢复啦. 我的原系统是Snow Leopard, 但买本的时候附带的是10.5.7的Leopard. 想图省事, 直接在安装程序选择从Time Machine恢复. 结果恢复完了以后, 进不去, Kernel Panic!! 我猜是用10.5.7恢复10.6.3的原因吧. 无奈只好先安装了10.5.7, 结果发现不能从Time Machine恢复高版本的系统. Migration Assistant也是. 此外我没有安装介质了. 原来倒还有一个10.6.0的dmg, 不过前阵子, 硬盘紧张, 手贱删掉了….囧…

终于啊终于, 今天早上6点醒来的时候发现10.6.3下好了. 于是restore到一个硬盘分区, 开始继续图省事—从Time Machine恢复, 结果又可耻的失败鸟…Log里一堆的错误, 可惜没记着把Log保存下来. 无奈只好不情愿的重装了, 点完install, 就去吃早饭了. 没想到, 吃完早饭回来, 竟然看到熟悉的壁纸–已经回到我换硬盘前的系统了! 完全一样. 没看到过程, 又有点遗憾. 不过我猜, 安装完后大概发现了有Time Machine的备份, 就直接整个还原回来了吧.

所以, 总结一下:

  • 事先备好工具.
  • 原来是什么版本的系统, 用什么版本的安装盘. 当然如果是想重装就无所谓了.
  • 不要直接在安装程序从Time Machine恢复, 问题多多.
  • 安装过程中, 连接上Time Machine的备份. 这样点完install, 你就可以该干嘛干嘛去了.
May
08
2010

我的拖延症

毕业快一年, 还是没有看完一本小说, 如果不算上以前看过的话. 大学四年也没有看完一本小说, 如果不算上不足2, 3万字的短篇小说的话. 一想到这样, 我就无比纠结, 我到底是在干什么呢? 这些年我到底做了什么呢? 我真是懒到骨头里了么?

不过细想一下, 还算是能找出一些算是有意义的事情, 只是实在是不够多. 在没有压力的情况下, 我几乎一事无成. 而正如很多同学朋友所说所想, 大家现在都已迫于现实的压力, 想要做出改变更加困难重重. 对自己的这种状态, 当然有过数不清次的思考与强迫, 有进步但收效仍不大. 所以我想我应该再清理下思维.

当然, 在某些方面, 进步还是有的. 至少我不会再如某三表所说的那样”瞎鸡巴怀疑人生”了. 这点在大学前只是觉得应该如此, 现在则是想当然的如此了.

好了, 主要问题当然在于我自己. 拖延症, 这就是症结所在. 试图克服, 如前面所说, 收效不大. 不过仔细想来, 问题出现的时间与出现问题时做的事情, 就一目了然了. 如果把”拖延症”当成一种由环境因素引起的生理疾病, 那么我以前认识到的是其症状以及通过加强自身免疫系统来治疗它. 但是现在的问题是加强了免疫系统, 还是压制不了由环境造成的病变. 最简单的做法显然是消除环境因素成了, 真奇怪我现在才想明白. 这个环境因素就是”Internet”.

除掉环境因素, 现实的做法就是限制上网时间, 以及控制上网时段. 更加精确的讲就是断绝一切信息的干扰, 浏览器显然是罪魁祸首, 此外就是Twitter, 再接下来就是各种IM. 此外, 还是要加强免疫系统, 而在这方面其实我有着丰富的经验. 自我安慰, 自我激励, 自我陶醉是我的拿手好戏, 它们也可以被叫做自欺欺人, 更现代的说法就是YY.

既然有了解决方法, 就暂行吧, 拭目以待明后天执行的效果吧.

Apr
02
2010

CouchDB: The Definitive Guide, 2-1, Design Documents (翻译+笔记)

设计文档

设计文档是CouchDB里的一种特殊文档, 它包含了应用代码. 因为它跑在数据库之内, 所以应用API是高度结构化的. 在前面的章节里, 我们已经看过了JavaScript视图以及其他的函数. 在这部分里, 我们来看看函数API, 然后来探讨下设计文档中的函数和应用程序是如何相关连的.

文档建模

在我的经验里, 有两种文档. 第一种文档就像那种用来保存的文字处理文档或者一个用户个人信息. 要处理这种文档, 你想要尽可能的非正常化它们. 基本的, 你会想要在一个请求里读取文档, 然后得到一些有用的东西显示出来.

有一种技术可以用来创建”虚拟”文档, 它通过使用视图把数据收集到一起来实现. 利用这个, 你可以把你的用户个人信息的每个属性存储到每个不同的文档中, 但是我不会推荐这样做. 当视图是通过合并不同作者的工作而创建时, 虚拟文档很用—比如, 我们的参考例子, 通过一个查询得到一篇博客文章和它的评论. CouchDB Joins, by Christopher Lenz, 涵盖了更多的细节.

这种”虚拟文档”的概念把我们带到了另一种文档—事件日志. 当你不信任用户输入或者需要做异步工作时, 使用它. 它以事件的形式记录用户动作, 所以在保存的时候只需要非常少的验证. It’s when you load the doc for further work that you’d check for complex relational-style constraints.

你可以把文档看成是状态机, 带有用户输入和后台进程来管理文档状态. 你可以通过状态使用视图来找出相关的文档—改变它的状态则会在视图中移动它.

这种实现对于做日志来说同样有用—和 batch=ok 的性能提示组合, CouchDB可以作为一个很好的日志存储器, 并且reduce视图对于找出像平均响应时间或者高活跃度用户之类的, 非常理想.

查询服务器

CouchDB的默认查询服务器(执行设计文档函数的软件包)是用JavaScript写的, 但是有几乎其他任何你可以想像到的语言的视图服务器. 实现一种新语言的服务器只是用简单几行程序处理几个JSON命令而已.

在这部分里, 我们会回顾已存在的功能, 像map reduce视图, 更新验证函数, 显示和列出变换. 我们还在简要的描述在CouchDB路线图上将会有的功能, 比如备份过滤, 非JSON输入的更新处理器, 以及为了让应用URL看起来更加美味的处理器重写.

在这一章节中, 我们会来看看设计文档, 以及它是如何嵌入CouchDB的架构来提供服务的. 在这里讲到的原则在扩展, 集成, 独立运行上都适用.

应用即文档

CouchDB被设计成当存在应用和设计文档之间有一一对应时, 工作的最好.

设计文档是指CouchDB中id以_design/开头的文档. 比如, 样例blog应用, Sofa, 以id _design/sofa存储在设计文档中. 设计文档和其他CouchDB相似: 它们和其他文档一样被备份, 并且同样通过rev参数跟踪编辑冲突.

就像我们已经看到的那样, 设计文档是普通的JSON文档, 不同的是它们的文档id的前缀是_design/.

CouchDB在这里寻找视图和其他应用函数. 我们应用的静态HTML页面作为设计文档的附件. 视图和验证, 不作为附件存储, 而是直接包含在设计文档的JSON体中.
Figure: Anatomy of our Design Doc
Figure: Anatomy of our Design Doc

CouchDB的Map Reduce查询存储在views域中. 这是为什么Futon显示并且允许你编辑Map Reduce查询的原理. 视图索引作为每个设计文档的基本存储, 根据函数文本内容的指纹. 这意味着, 如果你编辑了设计文档的附件, 验证, 或者其他不是view的(或是language的)域, 视图索引不会重新生成. 然而, 如果你改变了一个map或者一个reduce函数, 视图索引会被删除, 且一个新的视图索引会根据新的视图函数建立.

CouchDB可以返回纯JSON以外的格式. 设计文档的show和list域包含函数用于把纯JSON转化成HTML, XML或者其他的Content-Types. 这允许CouchDB不用其他中间件就可以作为一个Atom feeds. show和list函数有点像传统web框架中的”actions”, 会基于请求执行一些代码, 然后作出返回. 然而, 它们的不同这处是, 它们可能没有副作用. 这意味着, 它们主要被限制于处理GET请求, 但是也意味着它们可以被像Varnish这样的代理缓存.

因为应用逻辑被包含于一个单一的文档, 代码更新可以通过CouchDB的备份来完成. 这也打开了一个数据库托管多个应用的可能性. 一个报纸编辑需要的界面和一个读者想要大相径庭, 尽管数据大多是相同的. 它们可以被托管在同一个数据库, 不同的设计文档上.

一个CouchDB数据库可以包含许多设计文档. 示例设计文档id:

_design/calendar
_design/contacts
_design/blog
_design/admin

完整的CouchDB URL结构里, 你能够通过这样的URL来得到设计文档的JSON:

http://localhost:5984/mydb/_design/calendar

http://localhost:5984/mydb/_design/contacts

http://localhost:5984/mydb/_design/blog

http://localhost:5984/mydb/_design/admin

我们展示这个是为了让人们注意, 设计文档有一个特别的地方, 它们是唯一可以在URL里使用文本斜杠的文档. 我们这么做是因为没人喜欢在浏览器的地址栏里看到 %2F. 在其他地方, 带有斜杠的文档id必须在URL被转义. 比如文档id movie/jaws会显示成这样的URL:http://localhost:5984/mydb/movies%2Fjaws

我们在开发示例应用的第一个迭代里不会用到show和list, 因为通过JSON API来写Ajax查询可以更好的把CouchDB作为一个数据库来教学. 第一个迭代里我们使用的API和你用来分析日志数据, 实现判断或者管理持久化查询的API是一样的.

在第二个迭代里, 我们会升级我们的示例blog, 让它可以在客户端JavaScript关闭的情况下工作. 而现在, 使用Ajax查询可以给我们更多的透明度来理解CouchDB的JSON / HTTP API是如何工作的. JSON是JavaScript的一个子集, 所以它和JavaScript一起用遇到的阻力很小, 而浏览器的XMLHttpRequest(XHR)对象可以帮我们处理HTTP的细节.

CouchDB使用validate_doc_update函数来防止无效的或者未授权的文档更新. 我们在示例应用中使用它来保证blog post只能由登录用户管理. CouchDB的验证函数同样不能有任何副作用, 并且不仅可以阻止终端用户的文档保存, 还可以阻止来自其他节点的备份文档. 我们会在本书的第三部分详细讲验证.

Sofa所需要的原生的图片, JavaScript, CSS和HTML资源保存在_attachments域中, 有趣的是, 默认它只显示文件信息, 而不是全部的文件内容. 所有的CouchDB文档都可以有附件, 不仅仅是设计文档, 所以资源管理应用可以想要多灵活就有多灵活. 如果你的应用需要一组资源才能跑起来, 那它们应该被附加到设计文档. 这意味着一个新用户可以轻松的在一个空数据库里初始化你的应用.

上面展示的(以及我们将要使用的)设计文档的另外一些域是CouchApp为了方便放上去的. signatures域让我们可以避免更新硬盘和数据库间没有变化的附件—它通过比较文件内容散列来做到这一点. lib域用来保存附加的JavaScript代码和JSON数据, 用以在部署时插入到view, show以及validation函数里.

一个基本的设计文档

下个部分, 我们会进入到设计文档的高级技术, 但在此之前, 让我们先来看一个最基本的设计文档. 所有我们要做的就是定义一个单一的视图, 但是它应该已经足够向你展示设计文档是如何在更大的系统里作用的.

首先, 用你的编辑器把下面的文本(或者类似的)加到一个叫做mydesign.json的文本文件里

{
“_id” : “_design/example”,
“views” : {
“foo” : {
“map” : “function(doc){ emit(doc._id, doc._rev)}”
}
}
}

现在, 使用curl把这个文件PUT到CouchDB(我们会先创建一个数据库):

curl -X PUT http://127.0.0.1:5984/basic
curl -X PUT http://127.0.0.1:5984/basic/_design/example -d @mydesign.json

从第二个请求里, 你会看到类似的响应:

{“ok”:true,”id”:”_design/example”,”rev”:”1-230141dfa7e07c3dbfef0789bf11773a”}

现在, 我们可以查询我们定义的视图了, 在我们做这之前, 我们应该加一些文档到数据库里, 这样我们才会有东西来查询. 执行下面的命令几次会加入一些空文档.

curl -X POST http://127.0.0.1:5984/basic -d ‘{}’

现在, 再来查询视图:

curl http://127.0.0.1:5984/basic/_design/example/_view/foo

这应该会给你一个数据库时所有文档的列表(除了设计文档). 你已经创建和使用了你的第一个设计文档!

展望未来

还有其他的一些设计文档函数会在本书的进行过程中被介绍, _update和_filter我们不会在这里深入介绍. Filter函数会在Change Notificaions章节介绍. 想像一下, 当特定事件发生时, 一个web service(A) POST 一个XML-blob到一个你指定的URL. Paypal的实时支付通知是它们中的一个. 用一个_update处理器, 你可以在CouchDB里直接POST这些并且它可以把XML解析成JSON文档并保存它. CSV, 多表单以及其他格式也一样.

我们正在做就像是一个应用服务器, 但在一个关键点上不一样: 不是让开发者做任何他们想要做的(遍历文档id的列表, 然后做查询, 在其他查询的结果上再做查询, 等等), 而是我们定义好”安全”的转换, 像view, show, list和update. 这里所说的安全意思是, 它们有已知的很好的性能, 并且能高效的嵌入到CouchDB的架构里.

这里的目标是提供一种途径可以构建一个独立的应用, 它可以简单的被搜索引擎索引并且让用户使用. 可以放入古老的纯HTML. 你可以让JavaScript放心的执行(除非真的不行). 拥有HTML资源意味着, CouchDB适合于公共的网络应用.

只有一个重写处理器和一个数据库事件处理器, 因为它们似乎可以已经可以很漂亮的描绘出一个应用了. 重写处理器可以让你的应用展现自己的URL, 让它可以更容易的整合到现存系统. 事件处理器可以让你在数据库改变里执行异步处理, 比如, 一个文档更新可以触发一个工作流, 多文档验证或者消息队列.

Mar
05
2010

CouchDB: The Definitive Guide, 1-4, The Core API (翻译+笔记)

The Core API

这是章节会仔细的来探索CouchDB. 会展示所有的重要的实际的问题和聪明的处理方法. 我们会向你展示最佳实践并在一些常见的会遇到的问题是上指导你.

我们从重新回顾我们在前几个章节的操作开始, 看看这些操作的背后在做什么. 我们还会展示Futon在它的界面底下需要做些什么来提供给我们先前我们看到的美好特性.

这个章节同时是一个关于核心CouchDB API的介绍和一个参考. 如果你记不起来如何跑一个特殊请求或者为什么需要一些参数, 你总是可以回到这里来查找(我们可能是使用这一章节最多的用户.)

当我们在探索API时, 有时候我们需要绕一个弯路来解释一个特殊请求的原因. 对于我们来说, 这是一个告诉你为什么CouchDB这么工作的好机会.

API可以被分成下面的几个部分. 我们会分别来看它们:

  • 服务器信息
  • 数据库
  • 文档
  • 备份

数据库信息

这部分是基础的也是简单的. 它可以检查CouchDB是否正在工作. 它也可以作为一些需要特定版本CouchDB的库的安全保障. 我们会再一次用到`curl`这个工具.

curl http://127.0.0.1:5984/

CouchDB回应:

{“couchdb”:”Welcome”,”version”:”0.9.0″}

你会得到一个JSON字符串, 这个字符串, 如果把它作为你的编程语言的原生对象或者一个数据库解析, 你会得到一个welcome字符串和版本信息.

这不是非常有用, 但是它很好的展示了与CouchDB交互的一种方法. 你发送一个HTTP请求然后你会在HTTP回应里收到一个JSON字符串作为结果.

数据库

现在让我们做些更加有用的: 创建数据库. 严格的来说, CouchDB是一个数据库管理系统(DMS). 这意味着它可以有多个数据库. 一个数据库保存有”相关数据”的桶. 我们会在后面解释这到底意味着什么. 在实际中, 术语重叠了, 人们经常把DMS当成一个数据库, 也同时把一个在DMS里数据库当成DMS. 我们可能会顺着这种奇怪的现象, 不会被它搞混了, 通常, 能清楚的区分我们到底是在讲整个CouchDB还是一个在CouchDB里的数据库.

现在让我们来创建一个! 我们想要存储我们最喜欢的音乐专辑并富有创造性的把我们的数据库起名叫albums. 注意, 我们又一次使用了-X这个选项来告诉curl来发送一个PUT请求而不是默认的GET请求.

curl -X PUT http://127.0.0.1:5984/albums

CouchDB回应:

{“ok”:true}

That’s it. You created a database and CouchDB told you that all went well. What happens if you try to create a database that already exists? Let’s try to create that database again:
就是这样. 你创建了一个数据库然后CouchDB告诉你一切正常. 如果你试图创建一个已经存在的数据库会发生什么事? 我们来试试再创建同一个数据库:

curl -X PUT http://127.0.0.1:5984/albums

CouchDB回应:

{“error”:”file_exists”,”reason”:”The database could not be created, the file already exists.”}

我们得到了一个错误. 这相当方便. 我们同时学到了一点点关于CouchDB是如何工作的. CouchDB在一个单一文件里存储一个数据库. 非常简单. This has some consequences down the road, but we skip on details for now and explore the underlying storage system the The Power of B-Trees appendix.

让我们来创建另一个数据库, 这次带上curl的-v(“verbose”的简写)选项. verbose这个选项告诉curl不仅只显示必要的信息—HTTP的回复体, 还要显示请求与回复的细节:

curl -vX PUT http://127.0.0.1:5984/albums-backup

curl 详细显示:

* About to connect() to 127.0.0.1 port 5984 (#0)
* Trying 127.0.0.1… connected
* Connected to 127.0.0.1 (127.0.0.1) port 5984 (#0)
> PUT /albums-backup HTTP/1.1
> User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
> Host: 127.0.0.1:5984
> Accept: */*
>
< HTTP/1.1 201 Created
< Server: CouchDB/0.9.0 (Erlang OTP/R12B)
< Date: Sun, 05 Jul 2009 22:48:28 GMT
< Content-Type: text/plain;charset=utf-8
< Content-Length: 12
< Cache-Control: must-revalidate
<
{"ok":true}
* Connection #0 to host 127.0.0.1 left intact
* Closing connection #0

满满的一屏幕. 让我们一行行的看看来搞明白具体是在做什么并且找出哪些是重要的. 当你看过几次这个输出后, 你会更加容易的找出重要的部分.

* About to connect() to 127.0.0.1 port 5984 (#0)

这是curl在告诉我们正在向我们请求URI中的CouchDB服务器建立一个TCP连接. 没什么重要的地方, 只在调试网络问题的时候有用.

* Trying 127.0.0.1… connected
* Connected to 127.0.0.1 (127.0.0.1) port 5984 (#0)

curl 告诉我们成功的连接到了CouchDB. 再一次, 不重要, 如果你没有发现什么网络问题的话.

下面的几行有一个”>”或者”<"的前缀. ">“的意思是这几行被逐字发送到CouchDB(不包括”>”). “<"的意思是这些是CouchDB发送回给curl的.

> PUT /albums-backup HTTP/1.1

这行初始化一个HTTP请求. 它的方法是PUT, URI是 /albums-backup, HTTP版本是HTTP/1.1. 还有一个HTTP/1.0的版本, 它在某些情况下更简单, 但是因为各种现实原因, 我们应该使用HTTP/1.1.

接下来, 我们看到一些请求头. 这些是用来提供到CouchDB的请求的附加细节的.

> User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3

User-Agent头告诉CouchDB哪种客户端软件在作HTTP请求. 我们没有了解到什么新的, 就是curl. 在web开发中这个头经常很有用, 当知道服务器响应这个客户端实现的请求时存在问题时. 它也可以帮助决定, 用户是在哪个平台上的. 这个信息可以被用于技术和统计. 对于CouchDB来说, 这个头是无关的.

> Host: 127.0.0.1:5984

这个头是HTTP1.1需要的, 它告诉服务器请求的主机名.

> Accept: */*

Accept头告诉CouchDB, curl接受任何媒体类型. 我们会在后面来深入了解为什么这很有用.

>

一个空行表示请求头已经结束了, 剩下的请求包含我们要发送给数据库的数据. 在这个例子里, 我们不发送任何数据, 所以剩下的curl输出是HTTP响应的了.

< HTTP/1.1 201 Created

CouchDB的HTTP响应的第一行包含了HTTP版本信息(也让我们知道了, 请求的版本可以被处理). 一个HTTP状态码和一个状态码信息. 不同的请求会触发不同的返回状态码. 有一整个范围的状态码来告诉客户端(这个例子里是curl), 它作出的请求在服务器上做了什么. 或者如果有错误发生了, 告诉客户端什么错误发生了. RFC 2612, the HTTP 1.1 specification 清楚了定义了返回状态码的行为. CouchDB完全遵守这个RFC.

“201 Created”状态码告诉客户端请求要求创建的资源被成功的创建了. 没有什么值得惊奇的事, 但如果你还记得当我们试图两个创建这个数据库时我们得到了一个错误, 你就明白会有一个不同的返回码. 根据返回码做出处理是一个常见的做法. 比如, 所以400或400以上的返回码告诉你有什么错误发生了. 如果你想要简化你的逻辑并立即处理错误, 你可以只检查大于400的返回码.

< Server: CouchDB/0.9.0 (Erlang OTP/R12B)

Server头对于诊断很有用, 它告诉我们你是再和哪个版本的CouchDB和底层的哪个版本的Erlang打交道. 通常来说, 你可以忽略这个头, 但当你需要它的时候, 知道它在哪里也是好的.

< Date: Sun, 05 Jul 2009 22:48:28 GMT

Date头告诉你服务器的时间. 因为客户端和服务器端的时间没有必要一定同步, 这是头只是一个纯粹的信息而已. 你不应该根据这个信息为逻辑构建任何关键应用.

< Content-Type: text/plain;charset=utf-8

这个头告诉你HTTP响应体是什么原类型的和它的编码. 我们已经知道CouchDB返回JSON字符串. 合适的Content-Type是application/json. 为什么我们看到的是text/plain? 这是实用主义赢纯粹主义的地方. 发送一个applicaion/json的Content-Type头会使浏览器把返回的JSON提供给你下载而不是显示它. 因为可以在浏览器里测试CouchDB非常重要, CouchDB发送了一个text/plain的Content-Type, 这样浏览器就把JSON以文本的形式显示出来.

有一些浏览器插件可以让你的浏览器认得出JSON, 但是它们并不是默认安装的.

你还记得Accept请求头并且它是如何设置成”*/*”来表示它接受任何的源类型的吗? 如果你在你的请求里发送Accept: application/json, CouchDB认为你可以处理纯JSON响应, 会返回正确的Conten-Type头, 而不是text/plain.

< Content-Length: 12

这仅仅告诉我们响应体有多少字节.

< Cache-Control: must-revalidate

这告诉你或者任何在CouchDB和你之间的代理服务器不要缓存这个响应.

<

这个空行告诉我们响应头已经完了, 接下来的是响应体了.

{“ok”:true}

我们以前已经看到过这个了.

* Connection #0 to host 127.0.0.1 left intact
* Closing connection #0

最后两行是curl告诉我们它会保持TCP连接打开一会, 但是在接收完整个响应后关掉它.

贯穿于整本书, 我们会展示更多的带-v选项的请求, 但是忽略掉一些我们已经在这里看过的头, 并只包含那些对于那个特定请求来说重要的.

我们已经知道如何创建数据库了, 但是如何删除一个呢? 简单, 只要改变HTTP方法.

curl -vX DELETE http://127.0.0.1:5984/albums-backup

这会删除一个CouchDB数据库. 这个请求会存储数据库内容的文件. 删除数据库时, 没有”你确定吗”安全保障或者”清空垃圾箱”之类的魔法. 谨慎的使用这个命令. 你的数据会被删除, 并且如果你没有做备份, 就没有任何机会再轻易的来回来了.

这部分深入的讲解了HTTP并且为讲解剩下的CouchDB API搭建了舞台. 下一站: 文档.

文档

文档是CouchDB的中心数据结构. 文档背后的观念是, 没有什么令人惊奇的, 就是真实世界的文档. 像帐单, 发票或者名片一样的一张纸片. 我们已经知道了, CouchDB使用JSON格式存储文档. 让我们看看这种存储是底层是如何工作的.

CouchDB里的每个文档都会一个id. 每个数据库里这个id都是唯一的. 你可以选择任何字符串作为id, 但是最好的, 我们推荐使用UUID(或者GUID). Universally (or Globally) Unique IDentifier. UUID是一些极小可能重复的数字, 以至于每个人以每分钟产生成千上万个UUID的速度过几百万年都不会有重复产生. 这是一种非常棒的方法来保证两个独立的人不会产生有相同id的文档. 为什么你要关心其他人在干什么? 第一个原因, 那个”其他人”可能会是以后某个时间某台不同电脑上的你自己; 第二个原因, CouchDB备份让你使用UUID来和其他人分享文档, 用UUID可以保证它工作正常. 呆会我们再详细解释, 现在来创建几个文档.

curl -X PUT http://127.0.0.1:5984/albums/6e1295ed6c29495e54cc05947f18c8af -d ‘{“title”:”There is Nothing Left to Lose”,”artist”:”Foo Fighters”}’

CouchDB响应:

{“ok”:true,”id”:”6e1295ed6c29495e54cc05947f18c8af”,”rev”:”1-2902191555″}

这个curl命令看起来有些复杂, 我们来分解一下. 首先 -X PUT 告诉curl作一个PUT请求. 它后面跟着一个URL来指定你的CouchDB的IP地址和端口. URL的资源部分 /albums/6e1295ed6c29495e54cc05947f18c8af 指定了我们的albums数据库中文档的位置. 那串乱七八糟的数字和字母集合是一个UUID. 这个UUID是你的文档的id. 最后, -d 标志告诉 curl 用后面跟着的字符串来做PUT请求的body. 这个字符串是一个简单的JSON结构, 包括了标题和艺术家属性以及它们相应的值.

如果你手头上没有UUID, 你可以让CouchDB给你一个(实际上, 这正是我们刚才做的, 只不过没有向你展示出来). 仅仅需要发送一个GET请求到 /_uuids.

curl -X GET http://127.0.0.1:5984/_uuids

CouchDB响应:

{“uuids”:["6e1295ed6c29495e54cc05947f18c8af"]}

如果你需要多于一个的UUID, 你可以传入 ?count=10 的HTTP参数来请求10个UUID, 或者任何你想要的数字.

为了确认CouchDB是不是在对你撒谎说它已经保存了你的文档(通常它不会:), 试着用一个GET请求来得到它.

curl -X GET http://127.0.0.1:5984/albums/6e1295ed6c29495e54cc05947f18c8af

我们希望你能看出一种模式. CouchDB里的一切东西都有一个地址, 一个URI; 你使用不同的HTTP方法来操作这些URI.

CouchDB响应:

{“_id”:”6e1295ed6c29495e54cc05947f18c8af”,”_rev”:”1-2902191555″,”title”:”There is Nothing Left to Lose”,”artist”:”Foo Fighters”}

这和你要CouchDB保存的文档很相似, 很好. 但是你应该注意到了, CouchDB在JSON结构中加了两个域. 第一个是_id, 它的值是我们叫CouchDB保存的文档的UUID. 如果有包含文档的id的话, 我们也在这里得到了, 这很方便.

第二个是_rev. 它代表版本号.

版本

如果你想CouchDB里的一个文档, 你不是告诉它去找到特定文档中的一个域然后插入一个新值. 而是从CouchDB载入整个文档, 在JSON结构里作改变(或者是一个对象, 当你使用某个编程语言时), 然后把整个新版本的文档存回CouchDB. 每个版本由一个新的_rev值标识.

如果你想要更新或者删除一个文档, CouchDB会期望你会提供一个_rev域来标识你要改变哪个版本. 当CouchDB接受了一个更改以后, 它会产生一个新的版本号. 这种机制保证了, 万一有人在你对文档做更新之前做了一个你不知道的更新, CouchDB不会接受你的更新因为你可能会覆盖你以为不存在的数据. 或者简单点的说: 谁先保存了对一个文档的改变, 谁就赢了. 让我们来看看如果我们不提供一个_rev域会发生什么(这和提供一个过时的值是一样的).

curl -X PUT http://127.0.0.1:5984/albums/6e1295ed6c29495e54cc05947f18c8af -d ‘{“title”:”There is Nothing Left to Lose”,”artist”:”Foo Fighters”,”year”:”1997″}’

CouchDB响应:

{“error”:”conflict”,”reason”:”Document update conflict.”}

如果你看到了这个, 在JSON结构里加上你的文档的最新版本号:

curl -X PUT http://127.0.0.1:5984/albums/6e1295ed6c29495e54cc05947f18c8af -d ‘{“_rev”:”1-2902191555″,”title”:”There is Nothing Left to Lose”,”artist”:”Foo Fighters”,”year”:”1997″}’

现在你会发现为什么在你作了这个请求后, CouchDB返回_rev会是件很方便的事.
CouchDB响应:

{“ok”:true,”id”:”6e1295ed6c29495e54cc05947f18c8af”,”rev”:”2-2739352689″}

CouchDB接受了你的写请求并且它也产生了一个新的版本号. 版本号是文档的md5散列, 加上一个N-的前缀表示文档被更新的次数. 这对备份很有用. 具体看冲突管理章节.

There are multiple reasons why CouchDB uses this revision system that is also called Multi Version Concurrency Control (MVCC). They all work hand-in-hand and this is a good opportunity to explain some of them.

为什么CouchDB使用这种版本系统, 也被叫作多版本并发控制(MVCC), 有多个原因.

CouchDB使用的其中一个HTTP协议的特性是它的无状态性. 这是什么意思? 要和CouchDB交流, 你需要做出请求. 做一个请求包括了打开一个到CouchDB的网络连接, 交换字节然后关闭连接. 这些你每做一个请求都会重复一遍. 另外的协议允许你打开一个连接, 交换字节, 保持这个连接打开, 然后在此后交换更多的字节, 也许是根据你一开始交换字节里包含的内容, 最后关闭连接. 保持一个连接用于今后使用要求服务器作额外的工作. 一个常见的模式是, 在一个连接的生命周期里, 客户端有一个持久的, 静态的服务器的数据视图. 管理巨大量的并行的连接是一项极大工作量的活. HTTP连接通常是短生命周期的, 作出同样的保障会轻松很多. 结果就是, CouchDB可以处理更多的并发连接.

另外一个原因是这个模型在概念上更简单, 因此更加容易编程. CouchDB使用了更少的代码来达到目标, 而使用更少的代码总是好事, 因为固定行数代码的缺陷比例是固定的.

版本系统对于备份和存储机制也有积极的作用, 但我们将在本书的后面章节来讲到它们.

文档的细节方面

Now let’s have a closer look at our document creation requests with the curl -v-flag that was helpful when we explored the database API earlier. This is also a good opportunity to create more documents that we can use in later examples.

现在让我们用 curl 的 -v 标志来近距离的看看我们的文档创建请求, 这在之前我们探索数据库API的时候很有用. 这也是一个创建更多文档的好机会, 以便我们在今后的例子中使用.

我们会增加一些我们最喜欢的音乐专辑. 从 /_uuids 资源得到一个新的UUID. 如果你不记得这是怎么做了, 把书翻回去几页找找.

curl -vX PUT http://127.0.0.1:5984/albums/70b50bfa0a4b3aed1f8aff9e92dc16a0 -d ‘{“title”:”Blackened Sky”,”artist”:”Biffy Clyro”,”year”:2002}’

顺便提一下, 如果你正好知道更多的关于你最喜爱专辑的信息的话, 不要犹豫添加上这些属性. 也不要着急如果你不知道所有这些专辑的所有信息, CouchDB的无模式文档可以包含任何你知道的. 总之, 你应该放松, 不要去担心数据.

带着-v选项, CouchDB响应的重要部分看起来应该像是这样:

> PUT /albums/70b50bfa0a4b3aed1f8aff9e92dc16a0 HTTP/1.1
>
< HTTP/1.1 201 Created
< Location: http://127.0.0.1:5984/albums/70b50bfa0a4b3aed1f8aff9e92dc16a0
< Etag: "1-2248288203"
<
{"ok":true,"id":"70b50bfa0a4b3aed1f8aff9e92dc16a0","rev":"1-2248288203"}

在返回头中, 我们得到了一个201″创建” HTTP状态码, 这在之前我们创建数据库时我们也见过了. Location头告诉我们最新创建文档的完整URL. 而且有一个新的头; 来看看Etag先生. 在HTTP里, 一个Etag标识了一个资源的特定版本. 在这个例子里, 它标识了我们的新文档的一个特定版本. 听起来很熟悉? 是的, 从概念上讲, 一个Etag就是CouchDB文档的一个版本号, 所以CouchDB使用版本号作为Etag也没有什么可以惊讶的了. Etag在缓存系统中很有用, 我们会在第五部分, 扩展CouchDB中学会如何使用.

附件

CouchDB documents can have attachments just like an email message can have attachments. An attachment is identified by a name and includes its mime type (or content type) and the number of bytes the attachment contains. Attachments can be any data. It is easiest to think about attachments as files attached to a document. These files can be text, images, Word documents, music or movie files. Let’s make one.

CouchDB文档可以有附件, 就像email可以带附件一样. 一个附件由一个名字和它的mine类型(或者content类型)以及它的字节数来标识. 附件可以是任何数据. 最简单的理解是附件就是附加在文档上的文件. 这些文件可以是文本, 图像, Word文档, 音乐或者电影文档. 让我们来创建一个.

附件有它们自己的URL, 你可以把数据上传到那. 假设我们想要把一张专辑的封面添加到文档6e1295ed6c29495e54cc05947f18c8af, 并且假设封面文档是当前目录下的artwork.jpg.

> curl -vX PUT http://127.0.0.1:5984/albums/6e1295ed6c29495e54cc05947f18c8af/artwork.jpg?rev=2-2739352689 –data-binary @artwork.jpg -H “Content-Type: image/jpg”

-d@ 选项告诉 curl 去读取文档的内容放到HTTP请求体. 我们使用-H选项告诉CouchDB我们上传的是一个JPG文件. CouchDB会保存这个信息并且当我们请求这个文档的时候, 返回合适的头; 比如像这样的一个图像, 一个浏览器会显示这个图像而不会要你下载这个数据. 这在今后会变得很方便. 注意, 你需要提供你想到附加到的文档的当前版本号, 就和你更新一个文档时一样. 因为, 不管怎么样, 附加一些数据也是在改变这个文档.

如果你把你的浏览器指向`http://127.0.0.1:5984/albums/6e1295ed6c29495e54cc05947f18c8af/artwork.jpg `, 你应该会看到你的封面图片.

如果你再一次请求文档, 你会看到一个新的成员 _attachments:

curl http://127.0.0.1:5984/albums/6e1295ed6c29495e54cc05947f18c8af

CouchDB响应:

{“_id”:”6e1295ed6c29495e54cc05947f18c8af”,”_rev”:”3-131533518″,”title”:”There is Nothing Left to Lose”,”artist”:”Foo Fighters”,”year”:”1997″,”_attachments”:{“artwork.jpg”:{“stub”:true,”content_type”:”image/jpg”,”length”:52450}}}

_attachments是一个key和value的列表, value是包含附件原数据JSON对象. stub=true告诉我们, 这个附件只是一个原数据. 如果我们在请求一个文档时, 使用 ?attachments=true这个HTTP选项, 我们会得到一个包含附件数据的base64编码数据.

在我们探索CouchDB特性时, 会看到更多的文档请求选项. 比如备份, 我们的下一个主题.

备份

CouchDB备份是一个用于同步数据库的机制. 很像rsync(如果你熟悉这个的话)在本地或者网络上同步两个目录, 备份在本地或者远程同步两个数据库.

在一个简单的POST请求里, 你告诉CouchDB备份的源和目标, 然后CouchDB会在源上找出有哪些文档和哪些新文档版本是目标上没有的并且会把它们移到目标上.

我们会在本书的后面深入的探索备份;在本章节中, 我们只是向你展示如何使用它.

首先, 我们创建一个目标数据库. 注意, CouchDB不会自动的为你创建一个目标数据库而是会返回一个备份失败, 如果目标不存在的话(少了源的话也一样, 不过这个错误很不容易犯:).

curl -X PUT http://127.0.0.1:5984/albums-replica

现在我们可以使用数据库album-replica作为一个备份目标:

curl -vX POST http://127.0.0.1:5984/_replicate -d ‘{“source”:”albums”,”target”:”albums-replica”}’

CouchDB响应(这次我们对输出做了格式化, 这样你可以更加简单的读它):

{
“history”: [
{
"start_last_seq": 0,
"missing_found": 2,
"docs_read": 2,
"end_last_seq": 5,
"missing_checked": 2,
"docs_written": 2,
"doc_write_failures": 0,
"end_time": "Sat, 11 Jul 2009 17:36:21 GMT",
"start_time": "Sat, 11 Jul 2009 17:36:20 GMT"
}
],
“source_last_seq”: 5,
“session_id”: “924e75e914392343de89c99d29d06671″,
“ok”: true
}

CouchDB会维护一份备份的历史. 一个备份请求的回应会包含这个备份的历史备份. 备份请求会一直保持打开直到备份结束. 如果你有很多的文档, 这会花点时间, 直到它们都被备份了, 而且直到它们都被备份了, 你不会得到备份的响应. 有一点很重要的需要注意的是, 备份, 只会备份开始备份时的数据库的那个点. 所以, 任何在备份开始后的添加, 更改或者删除不会被备份.

We’ll punt on the details again, 最后的”ok”:true告诉我们一切顺利. 如果现在你看下albums-replica数据库, 你应该会看到所有你在albums数据库创建的文档.

你刚才所作的在CouchDB的术语里叫做本地备份. 你创建了一个本地的数据库的副本. 这对于备份, 或者保留一份数据在某个特定时间的快照以便日后使用来说是很有用的. 如果你在开发一个应用, 但是想在需要的时候可以返回到稳定的代码和数据版本, 你可能会想要做备份.

还有其他种类的复制, 在其他状况下有用. 我们备份的源和目标实际上是链接(就像在HTML里的那种), 而到目前为止, 我们看到的链接是指向我们正在工作的(就是本地的). 你也可以指定一个远程数据库作为目标:

curl -vX POST http://127.0.0.1:5984/_replicate -d ‘{“source”:”albums”,”target”:”http://127.0.0.1:5984/albums-replica”}’

使用一个本地源和一个远程目标数据库被叫做push replication. 我们把改变推到远程服务器.

想和远程服务器或者对门的家伙共享数据, 这方法好极了.

你也可以使用一个远程源和一个本地目标做pull replication. 想要拿到别人数据库上作的最新改变, 这也是个好办法.

curl -vX POST http://127.0.0.1:5984/_replicate -d ‘{“source”:”http://127.0.0.1:5984/albums-replica”,”target”:”albums”}’

最后, 你可以作远程备份, 这在管理操作时比较有用:

curl -vX POST http://127.0.0.1:5984/_replicate -d ‘{“source”:”http://127.0.0.1:5984/albums”,”target”:”http://127.0.0.1:5984/albums-replica”}’

收尾

这仍然不是完整的CouchDB API, 但是我们仔细的讨论了必要的部分. 我们将边走边把剩下的空白填上. 现在我相仿你已经准备构建CouchDB应用了.

Feb
22
2010

CouchDB: The Definitive Guide, 1-3, Getting Started (翻译+笔记)

Getting Started

在本节中, 我们将会快速的浏览下CouchDB的各种特性, 熟悉Futon, 一个内建的管理界面. 我们会创建第一个文档并体验CouchDB的视图. 在我们开始前, 跳到关于你的操作系统的安装附录. 在你继续前进之前, 按照这些指示, 安装好CouchDB.

任何系统都能跑

我们会用`curl`来快速的看看CouchDB的一些零散的API. 请注意这只是其中的一种和CouchDB沟通的方式. 我们在本书的剩下部分里展示更多的方式. `curl`的有趣之处在于, 它给了你原生HTTP请求的控制能力, 并且让你能准确的看到你的数据库底层到底在干些什么.

确认CouchDB正在工作, 然后输入:

curl http://127.0.0.1:5984/

这会发一个GET请求到你新装的CouchDB实例.

得到的回应看起来应该像这样子:

{“couchdb”:”Welcome”,”version”:”0.9.0″}

没什么特别的, CouchDB正向你说”Hello”呢, 并带着它的版本号.

接下来, 我们可以得到所有数据库的一个列表:

curl -X GET http://127.0.0.1:5984/_all_dbs

我们在前一个请求里加的只有”_all_dbs”这个字符串.
回应看起来像是这个样子:

[]

哦, 对, 我们还没有创建任何数据库! 我们看到的是一个空列表.

让我们拿创建一个数据库:

curl -X PUT http://127.0.0.1:5984/baseball

CouchDB会回应:

{“ok”:true}

Retrieving the list of databases again shows some useful results this time:
再取一个数据库列表, 这次会显示一些有用的结果:

curl -X GET http://127.0.0.1:5984/_all_dbs
["baseball"]

让我们创建另一个数据库:

curl -X PUT http://127.0.0.1:5984/baseball

CouchDB会回应:

{“error”:”db_exists”}

我们已经有了一个同名的数据库, 所以CouchDB会返回一个错误. 让我们试着换一个数据库名称.

curl -X PUT http://127.0.0.1:5984/plankton

CouchDB会回应:

{“ok”:true}

再一次获取数据库列表, 现在可以看到一些有用的东西了:

curl -X GET http://127.0.0.1:5984/_all_dbs

CouchDB会回应:

["baseball", "plankton"]

为了演示, 让我们删除第二个数据库:

curl -X DELETE http://127.0.0.1:5984/plankton

CouchDB会回应:

{“ok”:true}

现在数据库列表和先前的一样了:

curl -X GET http://127.0.0.1:5984/_all_dbs

CouchDB回应:

["baseball"]

为了减略, 我们跳过了和文档打交道的部分, 因为下一部分会讲到一个不同的但应该更加简单的方法来和CouchDB打交道, 那里会包含和文档打交道的这一部分. 当我们走过例子时, 记住, 在底层, 应用程序所作的事和你现在手工在做的是完全一样的. 任何事情都是用GET, PUT, POST和DELETE来操作一个URI.

欢迎来到Futon

在看过CouchDB的原生API后, 让我们来玩玩Futon, 内建的管理界面. Futon提供了到CouchDB特性的全部权限, 而且它让理解一些更加复杂的概念变得简单. 用Futon, 我们可以创建和销毁数据库, 查看和编辑文档, 建立和运行MapReduce视图, 还能在数据库之间进行备份.

想要在你的浏览器里打开Futon, 访问:

http://127.0.0.1:5984/_utils/

如果你跑的是0.9及其以后的版本, 你应该会看到像图3-1类似的界面. 在之后的章节里, 我们会关注用服务器端语言, 比如Ruby和Python来使用CouchDB. 而现在, 本节正是一个好机会, 可以展示一个只使用CouchDB的集成Web Server来构建动态Web应用的例子.

03-01
Figure 3-1: The Futon welcome screen
在全新安装一个CouchDB后的第一件事就是运行测试套件来确认正常工作. 这可以保证将来无论我们遇到什么问题都不是因为某些安装时的烦人原因而造成的. Futon测试套件里的失败会有一个红色标记, 告诉我们在使用一个可能有问题的数据库之前, 需要再三检查安装是否没有问题, 以免当出现不是我们所期待的事情时陷入混乱.

在Futon的侧边栏上点击Test Suite来进入测试套件, 然后点击主页面上端的run all来开始测试. 图3-2显示Futon测试套件正在跑一些测试.

03-02
Figure 3-2: The Futon test suite running some test

因为测试套件是从浏览器里跑的, 它不仅测试了CouchDB工作正常, 也验证了你的浏览器到数据库的连接设置正常, 这对于诊断不正常的代理或者其他HTTP中间件是很方便的.

好了, 测试套件跑完后, 你可以确认CouchDB安装已经成功, 你已经准备好来看看Futon还提供了哪些其他功能.

你的第一个数据库与文档

在Futon里创建一个数据库很简单. 在overview页面点击Create Database. 当问你叫什么名字时, 键入hello-world然后点击Create按钮.

03-03
Figure 3-3: An empty database in Futon

当你的数据库创建以后, Futon会显示一个该数据库全部文档的列表, 如图3-3所示. 这个列表一开始会是空的, 那么我们来创建第一个文档吧. 点击Create Document来创建. 确保让文档ID为空, CouchDB会为你生成一个UUID.

Futon会显示我们刚才新创建的文档, 这个文档只有_id和_rev两个域. 创建一个新的域, 点击Add Field. 我们把新的域叫作hello. 点击绿色的勾按钮(或者按回车)来结束创建hello域. 双击hello域的值(默认是null)来编辑它.

如果你键入world作为新值, 当你点击绿色勾勾时会得到一个错误. CouchDB的值必须是有效的JSON. 键入”world”(带双引号), 因为这是一个有效的JSON字符串, 这次保存它应该没有问题了. 你可以用另外的JSON值来试验一下, 比如 [1, 2, "c"] 或者 {“foo”:”bar”}. 一旦你键入了值, 注意下_rev属性然后点击Save Document. 结果应该像图3-4所示.

03-04
Figure 3-4: A “hello world” document in Futon

你会注意到文档的_rev已经改变了. 在后面的章节中我们会更细节的来看这个问题, 但是现在, 值得注意的事是当保存一个文档时, _rev表现为一个安全特性. 当你和CouchDB对文档的最新_rev达成一致, 你就能成功的保存改变.

Futon也提供了一种方法来显示底层的JSON数据, 根据你在处理的是什么类型的数据, 可以展示的更紧凑并且更易读. 要看我们的Hello World文档的JSON版本, 点击Source tab, 结果应该像图3-5所示.

03-05
Figure 3-5: The JSON source of a “hello world” document in Futon

用MapReduce执行查询

传统关系数据库允许你跑任何你喜欢的查询, 只要你的数据是正常的结构化的. 而CouchDB则使用预告定义的map和reduce函数, 一种被叫做MapReduce的风格. 这些函数提供了巨大的灵活性, 因为它们可以根据文档结构来产生不同变种, 并且每个文档的索引可以被独立和平行的计算. 一个map和一个reduce函数的组合在CouchDB的术语里被叫做视图.

Map函数每次调用都会用一个文档作为参数. 函数可以选择跳过整个文档或者使用一行或多行key/value对. Map函数可以不依赖任何文档之外的信息. 这种独立性使得CouchDB视图可以增量和平行的生成.

CouchDB视图是以key排序以行的形式被存储的. 这使得从一个keys范围中获取数据变得高效, 甚至当有成千上万行的时候. 在写CouchDB map函数的时候, 你的首要目标是建立一个索引, 这个索引存储相近key之间的相关数据.

在我们执行一个MapReduce视图示例之前, 我们需要一些用来执行这个示例的数据. 我们会创建包含不同超市不同商品的价格的文档. 让我们为苹果, 村子和香蕉来创建文档. (允许CouchDB来生成_id和_rev域.) 使用Futon来创建文档, 最后产生的JSON结构看起来是这样:

{
“_id” : “bc2a41170621c326ec68382f846d5764″,
“_rev” : “2612672603″,
“item” : “apple”,
“prices” : {
“Fresh Mart” : 1.59,
“Price Max” : 5.99,
“Apples Express” : 0.79
}
}

这个文档应该如图3-6所示.

03-06
Figure 3-6: An example document with apple prices in Futon

好, 让我们再来创建桔子的文档:

{
“_id” : “bc2a41170621c326ec68382f846d5764″,
“_rev” : “2612672603″,
“item” : “orange”,
“prices” : {
“Fresh Mart” : 1.99,
“Price Max” : 3.19,
“Citrus Circus” : 1.09
}
}

最后, 香蕉的:

{
“_id” : “bc2a41170621c326ec68382f846d5764″,
“_rev” : “2612672603″,
“item” : “banana”,
“prices” : {
“Fresh Mart” : 1.99,
“Price Max” : 0.79,
“Banana Montana” : 4.22
}
}

想像一下, 我们正在做一个大型的午餐, 但是客户对价格很敏感. 为了找出最低的价格, 我们来创建我们的第一个视图, 它会根据价格排序显示每种水果. 点击hello-world来回到hello-world的overview页面, 然后从视图选择目录里选择Temporary view…来创建一个新视图. 结果应该如图3-7所示.

03-07
Figure 3-7: A temporary view in Futon

编辑左边的map函数, 让它看起像这样子:

function(doc) {
var store, price, value;
if (doc.item && doc.prices) {
for (store in doc.prices) {
price = doc.prices[store];
value = [doc.item, store];
emit(price, value);
}
}
}

这是一个JavaScript函数, CouchDB在计算文档时, 都会跑这个函数. 现在我们把Reduce函数暂时留空.

点击Run, 然后你应该会看到如图3-8的结果, 以价格排序的不同的物件. 如果把这些物件用类型排序, 这个map函数会更加有用, 这样所以香蕉的价格在结果集里都集中了. CouchDB的key排序系统允许任何有效的JSON对象作为一个key.

03-08
Figure 3-8: The results of running a view in Futon

让我们修改视图函数, 使它变成这样:

function(doc) {
var store, price, key;
if (doc.item && doc.prices) {
for (store in doc.prices) {
price = doc.prices[store];
key = [doc.item, price];
emit(key, store);
}
}
}

在这个函数中, 我们首先检查文档是否有我们想要使用的域. CouchDB可以从一些独立的map函数失败中优雅的恢复, 但是当一个map函数正常的失败(因为缺少需要的域或者其他JavaScript异常), CouchDB 会关闭它的索引来防止进一步的资源使用. 因为这个原因, 在使用它们之前检查域的存在是很重要的. 在这个例子里, 我们的map函数会跳过我们第一个”hello world”文档, 不显示任何一列也不会遇到错误. 这个查询的结果应该如图3-9所示.

03-09
Figure 3-9: The results of running a view after grouping by item type and price

当我们知道我们得到了一个有物件类型和价格的文档, 我们遍历物价的价格然后显示key/value对. key是一个物件和价格的数组. 在这个例子中, 值是可以找到这个价格的物件的商场的名字.

视图列表根据它们的key来排序, 在这个例子中: 首先是物件, 然后是价格. 这种复杂排序的方法是创建有用的CouchDB视图的核心.

做备份

Futon可以在两个本地数据库, 一个本地数据库一个远端数据库, 或者甚至是两个远端数据库之间进行备份. 我们会向你展示, 如何从一个本地数据库备份数据到另一个, 这是一个简单的做数据库备份的方法.

首先我们需要创建一个空数据库作为备份目标数据库. 回到overview页面然后创建一个叫hello-replication的数据库. 现在点击侧边栏里的Replicator, 并选择hello-world作为源, hello-replication作为目标. 点击Relicate来备份你的数据库. 结果应该如图3-10所示.

03-10
Figure 3-10: Running database replication in Futon

收尾

现在我们已经看过了Futon的大部分功能, 你已经准备好更加深入并且在我们在后面章节构建我们的示例应用时检查你的数据. Futon的纯JavaScript管理CouchDB的实现显示了如何只用CouchDB的HTTP API和内建Web Server来构建一个全功能的Web应用.

但在我们到那之前, 我们用另一种眼光来看看CouchDB的HTTP API; 这次用放大镜. 让我们用curl来看看.

Feb
05
2010

Lighttpd用Fastcgi跑MoinMoin1.9注意事项

MoinMoin是一个相当好用的轻量级Wiki, 它是用Python写的, 没有数据库支持, 文本保存数据. 我是从Apple4Us的一篇工具癖里知道它的, 用它来作一些笔记. 建议本地使用, 只要`python wikiserver.py`就可以了.

前几天Tx说他在用Fastcgi跑MoinMoin时, 遇到了500错误. 恰好我VPS上的Web应用都是用的Lighttpd跑的, 就也试了一下. 我也遇到了500错误. 参考了MoinMoin的Wiki和Google, 发现都是针对1.8版本的. 我就总结下可能会遇到的问题:

1, 1.8版本的MoinMoin有一个moin.fcg的fastcgi启动脚本, 而1.9版本全部放在moin.cgi里了, moin.fcg被拿掉了. 500错误很可能的一个原因就是, 在WebServer上配置成了fastcgi, 而在moin.cgi里却没有. 改下里面的配置即可…如下所示.

os.environ['FCGI_FORCE_CGI'] = ‘N’ # ‘Y’ for (slow) CGI, ‘N’ for FCGI

2, 跑一下moin.cgi, 如果没有错误, 那应该就可以了. 如果有错, 参考它给出的错误信息改正. 一般会是权限问题, 或者文件路径的问题.

3, 如果静态文件(.css, .js等)没法正确解析的话, 就在wikiconfig.py里把”url_prefix_static”写死.

4, 安全注意, 不要把”data”, “underlay”, “wikiconfig.py” 这两个目录和这一个文件放在Web Server的根目录下…

题外话:

如果放在Internet上的话, 还是不喜欢用纯文本的方式, 虽然Apache Wiki也用MoinMoin在跑. 如果能用CouchDB作存储的话, 那就太棒了. Wiki这种文档式的应用和CouchDB简直是绝配.

Jan
31
2010

CouchDB: The Definitive Guide, 1-2, Eventual Consistency (翻译+笔记)

最终一致性

在上一章节中, 我们看到CouchDB的灵活性允许我们可以随着我们应用程序的增长和变化来演进我们的数据. 在这一章节中, 我们会了解如何在我们的应用中, 顺着CouchDB的简单性这一特性来工作, 并且帮助我们自然而然的构建可扩展的, 分布式的系统.

按照套路来工作

分布式系统是一个可以在一个广泛的网络上健壮工作的系统. 网络计算的一个明显的特点就是网络连接可能会断掉, 而且有多种策略来处理这个网络错误. CouchDB不同于其他的地方在于, 它接受最终一致性, 和把绝对一致性放在资源可用性之前(像关系型数据库管理系统或者Paxos算法)正好相反. 但这些系统的共同之处在于它们都关心当多人同时访问时的数据表现不一致. 它们的实现不同在, 它们在一致性, 可用性或者可分割性上的优先级.

开发分布式系统是不容易的. 许多你会遇到的警告和”gotchas” 不会马上显现. 我们没有全部的解决办法, CouchDB也不是万能药, 但是如果你顺着CouchDB的套路而不是反着它, 那么一条最少阻碍的道路会把你引向自然而然的可扩展的应用.

当然, 构建一个公布式系统只是一个开始. 一个网站, 用着只有一半时间有用的数据库, 基本没有价值. 不幸的是, 传统的关系数据库对于一致性的实现使得它很容易让程序员依赖于全局状态, 全局时钟等, 却没有意识到自己正在这么做. 在检查CouchDB是如何提升可扩展性之前, 我们来看一看一个分布式系统所面临的各种约束. After we’ve seen the problems that arise when parts of your application can’t rely on being in constant contact with each other, we’ll see that CouchDB provides an intuitive and useful way for modeling applications around high-availability.

CAP 理论

CAP理论描述了基于网络的分布式系统的几种不同的策略. CouchDB 的解决方法是使用备份在各个参与节点间同步应用的改变. 这是一个与一致性算法和关系数据库在根本上不同的实现, 它们的不同在于, 一致性, 可用性和可分割性的交叉点.
2-1
Figure 2-1: The CAP theorem

CAP理论, 如图2-1所示, 指出了三个明显不同的关心点:

一致性
所有的数据库客户端都看到相同的数据, 即使存在并发的更新.

可用性
所有的数据库客户端总能得到某一版本的数据.

可分割性
数据库可以被分割在多个服务器上.

选择其中的两个.

当一个系统增长到足够大, 大到一个单一的数据库节点不能处理给它的负载前, 一个很显然的办法就是增加更加的服务器. 当我们增加节点, 我们不得不开始考虑, 如何在节点间分割数据. 我们是在这些数据库上放置完全相同的数据? 还是把不同部分的数据放在不同的数据库服务器上? 还是应该只让一些确定的数据库服务来写数据而另外一些则处理读的请求?

不管我们选择哪种实现, 一个我们一直会遇到的问题是保持所有这些数据库服务器的同步. 如果你在一个节点上写了某些数据, 你要如何保证, 一个到另一个数据库服务器的读请求会反映这个最新的信息? 这些事件可能会发生在几毫秒之内. 即使只有少量的数据库服务器集, 这个问题也是相当复杂的.

当所有客户端都看到数据库的同一视图这一点非常重要的时候, 一个节点的用户在读和写数据库之前, 不得不等待其他节点的用户的同意. 在这个例子中, 我们可以看到, 可用性比一致性更加不重要. 然而, 也有可用性比一致性来的重要的状况.

“Each node in a system should be able to make decisions purely based on local state. If you need to do something under high load with failures occurring and you need to reach agreement, you’re lost… If you’re concerned about scalability, any algorithm that forces you to run agreement will eventually become your bottleneck. Take that as a given.”

— Werner Vogels, Amazon CTO and Vice President

如果把可用性优先来考虑, 我们可以让客户端在不等待其他节点同意的情况下就把数据写入一个数据库节点. 如果数据库知道在这些操作后, 如何保持数据库节点间的一致性, 那么我们就达到了一种”最终一致性”, 以此为交换, 得到了高可用性. 对于很多应用来说, 这是一个相当可接受的交易.

不像关系数据库, 每一个操作都会导致整个数据库层面的一致性检查, CouchDB让构建那些以牺牲限时一致性来换取巨大的性能提升的应用变得简单.

本地一致性

在我们去了解CouchDB是如何在集群上工作之前, 理解单一CouchDB节点的内部工作原理很重要. CouchDB API 的设计提供了一个方便的, 但是瘦小的, 封装的数据库核心. 通过仔细的了解数据库核心结构, 我们会对它的API有一个更好的理解.

数据的钥匙

在CouchDB的核心, 是一个强大的B-Tree存储引擎. 一个 B-Tree 是一个经过排序的数据结构, 它允许搜索, 插入和删除(in logarithmic time). 如图2-2所示, CouchDB的所有内部数据, 文档和视图都使用这个B-Tree存储引擎. 如果我们明白了其中一个, 我们就明白了全部.
2-2
Figure 2-2: Anatomy of a View Request
CouchDB使用 MapReduce 来计算一个视图的结果. MapReduce用了两个过程, “map”和”reduce”, 它们被独立的用于每个文档. 可以独立的进行这些操作意味着视图计算本身可以是平行的和增量的. 更加重要的是, 因为这些过程会产生 key/value 对, CouchDB就可以把它们插入到B-Tree引擎中, 并以key来排序. 在B-Tree中, 通过key, 或者key范围来查找, 效率非常高, 用大O表示法来描述的话分别是, O(log N) 和 O(log N + K).

在CouchDB里, 我们通过key或者key范围来得到文档和视图. 这是基于CouchDB的B-Tree引擎操作的一种直接映射. 和文档插入与更新一起, 这种直接映射是我们把CouchDB API描述成一个数据库核心的轻量的封装的原因.

可以通过单独的key来得到结果是一个非常重要的限制, 因为它允许我们得到巨大的性能提升. 还有巨大的速度提升, 我们可以把我们的数据分割到多个节点, 但却不会影响到每个节点独立查找数据的能力. BigTable, Hadoop, SimpleDB, 还有memcached限制通过key来查找对象也是因为这些原因.

无锁的

关系数据库里的一张表是一个单一的数据结构. 如果你想要修改一张表, 比如更新一行, 数据库系统必须保证没有其他正在更新那一行并且没有人可以读正在更新的这一行. 处理这种情况的一般方法就是所谓的锁机制. 如果多个客户端想到用这张表, 第一个客户端会得到一个锁, 这会让其他人全部等待. 当第一个客户端处理完成后, 下一个得到许可, 其他人继续等待, 如此往下. 这种顺序的请求处理方式, 即使是在它们同时到达的时候, 也会浪费掉你服务的相当可观的处理能力. 在高负载下, 一个关系数据库可能会把更多的时候花费在, 谁被允许干什么事, 是以怎样一个顺序, 而大于真正要做的事情的时间.

2-3
Figure 2-3: MVCC means no locking
作为锁机制的代替, CouchDB使用了多版本并发控制(MVCC)来管理对于数据库的关发请求. 图2-3形象描述了MVCC和传统锁机制的不同. MVCC意味着可以一直, 甚至在高负载的情况下全速运行. 请求平行的被处理, 榨干你机器提供的最终一滴处理能力…

CouchDB里的文档是可版本的, 很像一个常规的版本控制系统, 比如Subversion. 如果你想改变一个文档的value, 你会创建一个全新的文档, 然后保存在原来的文档之上. 这么做了之后, 你等于有一个同一文档的两个版本, 一个老的和一个新的.

这种方式与锁机制比改进在哪里呢? 考虑下这种情况, 一堆请求想要同一个文档. 第一个请求读这个文档. 当这个请求正在被处理时, 第二个请求改变了这个方法. 因为第二个请求包含了一个全新版本的文档, CouchDB可以简单的把它附加到数据库, 而不用等待那个读请求结束.

当第三个请求想要读这个文档时, CouchDB会把它指向一个那个被写入的新版本. 在这整个过程中, 第一个请求可以仍旧在读那个原始的版本.

一个读请求总是会看到最近的数据库快照.

验证

作为应用开发者, 我们必须要考虑哪些请求我们是接受的, 哪些是要拒绝的. 在传统关系数据库中, 对复杂数据作这种有效的验证的能力, 很有诱惑力. 幸运的是, CouchDB提供了一种强大的方法, 可以在数据库中做预文档的验证.

CouchDB可以使用类似于那些用于MapReduce的JavaScript方法来验证文档. 每当你试图去修改一个文档, CouchDB会传给验证方法一个现存文档的副本, 一个新文档的副本, 和一个附加信息的集合, 比如用户认证细节. 然后, 验证方法可以来通过或者拒绝这个更新.

顺着这种套路, 让CouchDB帮你做这些工作, 我们为自己节省了极大的CPU时间, 这些时间原本会被用在, 从SQL顺序化对象图表, 把它们转化成域对象, 然后使用这些对象做应用层的验证

分布一致性

保持单一数据库节点的一致性对于大多数数据库来说都相对简单. 当你试图在多个数据库数据库之前保持一致性时, 真正的问题开始浮现. 如果一个客户端在服务器A上做了一个写操作, 我们怎么样才能保证它和服务器B, C或者D保持一致? 对于关系数据库来说, 这是一个非常复杂的问题, 可以用一整本书来阐述它的解决办法. 你可以使用多multi-master, master/slave, partitioning, sharding, write-through caches, 和其他的复杂的技术.

增量备份

因为CouchDB操作是在一个单一文档的上下文中进行的, 如果你想要使用两个数据库节点, 你不需要去担心它们正处于实时通讯中. CouchDB通过增量备份, 一个在各个服务器之间周期性的复制文档变化的过程, 来实现这种最终一致性. 我们可以建立那种有名的shared nothing数据库集群, 在那里每个节点是独立并且自我满足的, 不会有系统级的竞争.

想要扩展你的CouchDB数据库集群? 只要扔进另外一个服务器就行了.

2-4
Figure 2-4: Incremental replication between CouchDB nodes
如图2-4所示, 使用CouchDB的增量备份, 你可以用任何你喜欢的方式, 任何你喜欢的时间, 在任意两个数据库之间同步你的数据. 备份完成后, 每个数据库都可以独立工作.

你可以使用这个特性,在一个集群或者数据中心之间, 用像cron这样的工作排程来同步数据库, 或者你也可以用它把数据同步到你的笔记本, 然后再旅行的时候作离线工作使用. 每个数据库都可以像平常那样使用而且数据库间的变化可以在以后双向同步.

如果你在两个数据库中改变了同一个文档并且想要同步它们, 怎么办? CouchDB的备份系统拥有自动的冲突检测和解决方法. 当CouchDB发现两个数据库中的文档都被改变了, 它把这个文档标记成冲突状态, 很像一个常规的版本控制系统所做的那样.

这个问题不像第一次听起来那样麻烦. 如果在备份中有一个文档的两个版本冲突了, 胜出的版本是那个在文档历史中最近被保存的那个版本. 不像你可能想像的那样, 把输了的那个版本丢弃, CouchDB把这个版本在文档历史中保存为上一个版本, 这样如果你需要的话就可以得到它. 这是自动和实时的, 所以两个数据库会作出完全相同的选择.

对于你的应用如何处理冲突更好取决于你自己. 你可以按默认的解决冲突, 也可以把文档版本转而更老的那个, 或者试着把两个版本合并然后存储这个结果.

案例分析

Greg Borenstein, 我们的一个朋友和同事, 写了一个库用来把Songbird播放列表转化成JSON对象, 并决定把它们存储到CouchDB中, 作为一个备份应用的一部分. 整个软件使用了CouchDB的MVCC和文档版本来保证Songbird播放列表在节点间健壮的被备份.

让我们来看一下Songbird备份应用的整个工作流程, 首先一个用户从一个单一的计算机备份了播放列表, 然后用Songbird在多个计算机之间同步播放列表. 我们可以看到文档版本如何把一个麻烦的问题变成一件自然而然的事情.

2-5
Figure 2-5: Backing up to a single database
第一次我们使用备份程序时, 我们导入我们的播放列表然后初始化一个备份. 每个播放列表被转化成一个JSON对象然后提交给CouchDB数据库. 如图2-5所示, CouchDB返回保存到数据库的每个播放列表文档的ID和版本号.

几天以后, 我们发现我们的播放列表已经更新了, 我们想要备份这些改变. 在我们把播放列表导入到备份程序后, 它会去CouchDB取最新的版本, 同时能到相应的文档版本. 当应用程序提交新播放列表文档时, CouchDB接受这个文档, 而文档的版本已经包含在请求中了.

然后, CouchDB会保证请求里提交给它的文档版本和数据库里保存的文档版本一致. 因为CouchDB在每个修改后都会更新版本, 如果这两个版本不同步, 这说明有人在我们请求这个文档和发送我们的更新之间, 对这个文档做了改变. 当有人改变文档后, 不首先检查这些改变就去改变这个文档通常会是个坏主意.

强制要求客户端返回正确的文档版本是CouchDB乐观并发的中心.

2-6
Figure 2-6: Synchronizing between two databases
我们有一个笔记本, 我们想要和我们的桌面计算机同步. 为了同步我们在桌面计算机上的所有播放列表, 第一步是在我们的笔记本上选择”从备份恢复”. 这是我们第一次做, 所以做完后我们应该会得到和我们的桌面计算机播放列表完全一个的一个副本.

当在笔记本上编辑了Argentine Tango这个播放列表, 增加了一些我们买的新歌之后, 我们想要保存这些改变. 备份程序在笔记本上的CouchDB数据库中替代掉播放列表文档, 一个新文档版本产生了. 几天后我们想起了这些新歌, 想要把播放列表复制到桌面计算机. 如图2-6所示, 备份程序统计复制新文档和新版本到桌面计算机的CouchDB数据库. 现在两个CouchDB数据库都有相同的版本号.

因为CouchDB跟踪文档版本号, 它保证像这样的更新只会在基于当前信息的基础上才会工作. 如果我们在同步过程之中, 对播放列表备份文档作出了改变, 事情就不会那么顺利了.

2-7
Figure 2-7: Synchronization conflicts between two databases
我们在笔记本上备份了一些改变然后忘记同步了. 几天后, 我们在桌面计算机上编辑了我们的播放列表, 做了个备份, 然后想要和我们的笔记本同步了. 如图2-7所示, 当我们的应用程序想要在两个数据库之间备份时, CouchDB发现从桌面计算机上发来的文档是经过改变的, 然后告诉我们有一个冲突存在.

从应用层的观点来看, 从这个错误里恢复很容易做到. 只要下载CouchDB播放列表的版本, 然后可以合并这些改变或者在一个新的播放列表里存储本地改变.

收尾

CouchDB的设计从Web架构里借用了很多东西, 并且从在Web架构上部署大型分布式系统中学到了很多. 通过理解为什么这种架构可以按照它的方式运行, 通过学习观察你应用程序的哪些部分容易做分布, 哪些部分不容易, 你将增强你设计分布式的和可扩展的应用程序的能力––––使用CouchDB或者不使用它.

我们已经了解了CouchDB一致性模型的主要议题, 并提示了顺着CouchDB工作而不是逆着它得到的好处. 但是, 理论已经足够了, 让我们起来看看所有这些到底是什么.

Jan
29
2010

Tor中继(Tor Relay)配置及流量控制

因为VPS的流量一直用不光, 所以开了个Tor中继(Tor Relay), 算是做点贡献. 第一天没有做任何限制, 12个小时左右就用掉了差不多40G的流量, 用Tor的人还真是多. 我用的是Linode的最小的VPS, 每月200G的流量, 因此承受不起. 所以我只能限制流量.

Torrc的注释其实已经自我解释的很清楚了. 要想成为一个中继, 去掉几行注释就完成了.

ORPort 9001

去掉这个注释, 然后重启Tor, 你就成为一个中继了, 非常简单.

RelayBandwidthRate 100 KBytes

RelayBandwidthBurst 200 KBytes

速度限制, Tor要求你的速度至少不低于20K. 我不大清楚Burst的意思, 是最大的速度还是瞬间允许达到的最大速率? 不过个人建议如果你有流量方面的问题, 那么将速度限制在40-50K, 这样就保证了浏览网页的流畅度, 也可以在一定的流量里让更多的Tor用户连接上你的中继.

AccountingMax 4 GBytes

AccountingStart day 00:00

AccountingStart month 3 15:00

流量控制, 我们先解释第二行与第三行. Tor的流量控制是指在一定的时间段内, 可以用多少流量. 而第二行与第三行就是来说明这个”一定时间段”的. 第二行的意思是, 每天的0点到下一个0点, 算是一个时间段; 第三行的意思是, 每个月3号的15:00到下个月的3号15:00算是一个时间段. 接着我们可以来解释第一行的意思了. 显而易见, 这是限制最大流量的. 需要注意的是, 这个流量限制是指在”一定时间段”里的流量, 并且这个流量是同时指”发出”与”收到”的. 也就是说如果你限制了4GB, 最大可能用掉的流量可能会是8GB(发出流量4GB, 接收流量4GB). 当你指定的流量用光时, Tor会自动休眠, 直到下一个”一定时间段”重新复活, 并接受请求.

DirPort 9030

DirPortFrontPage /etc/tor/exit-notice.html

成为一个目录镜像, 建议启用, 可以帮助Tor用户更好的发现其他中继. 第二行如果启用的话, 当有人访问你的IP:DirPort时会看到exit-notice.html这个网页, 告诉他们为什么你的IP在连接他的电脑.

还有出口策略可以设置, 暂时没有做. 有时间再研究了.

 
Powered by Wordpress and MySQL. Theme by openark.org