Apache的ProxyPass指令详解

原文地址:http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxypass
翻译:ShiningRay

描述:将远程服务器映射到本地服务器的URL空间
语法:ProxyPass [路径] !|url [键=值 键=值 …]] [nocanon]
上下文: server config, virtual host, directory
状态:扩展
模块:mod_proxy

该指令可以将远程服务器映射到本地服务器的URL空间;本地的服务器并不是扮演传统意义上的代理服务器的角色,而是表现为远程服务器的一个镜像。此本地服务器常被成为反向代理(reversed proxy)或者是网关(gateway)。路径是指本地虚拟路径的名字;url指远程服务器的一个部分URL,不能包含查询字符串。

注意:在使用ProxyPass指令的时候,ProxyRequests指令通常都应该是关闭的。

假设本地服务器的地址是 http://example.com/ ,那么

将会把对http://example.com/mirror/foo/bar的本地请求内部转换成到http://backend.example.com/bar的代理请求。

其中,!指令当你不想对某个子目录进行反向代理的时候就有用了,例如:

将会把所有的/mirror/foo请求重定向到backend.example.com除了对/mirror/foo/i的请求。

注意:

顺序很重要:排除的指令必须在一般的ProxyPass指令之前。

和Apache 2.1一样,具备了使用到后端服务器的线程池的能力。使用“键=值”的参数便可调节线程池的分配。硬性最大值(Hard Maximum)的默认值为当前MPM中每个进程的线程数量。在Prefork MPM中,该值总是为1,在Worker MPM中,该值受ThreadsPerChild控制。

设置min可以决定有多少到后端服务器的链接始终打开。根据需要最多创建数量为柔性最大值(Soft Maximum),也就是smax的链接。任何超出smax数量的链接都会指定一个生存时间也就是ttl。Apache不会同时创建超过硬性最大值(或max)数量的到后端服务器的链接。

参数 默认值 描述
min 0 到后端服务器的总是打开的链接最小数。
max 1…n 允许连接到后端服务器的硬性最大链接数。硬性最大值的默认值为当前MPM中每个进程的线程数量。在Prefork MPM中,该值总是为1,在Worker MPM中,该值受ThreadsPerChild控制。
smax max 根据需要最多创建数量为柔性最大值(Soft Maximum),也就是smax的链接。任何超出smax数量的链接都会指定一个生存时间也就是ttl
acquire 如果设置了该参数,它就会成为等待链接池中空闲链接的最大时间。如果链接池中没有任何空闲链接,那么Apache将会给客户端返回SERVER_BUSY状态。
flushpackets off 决定代理模块是否要在接受到每段数据的时候自动冲掉输出缓冲。‘off’表示只有当需要的时候才冲掉,‘on’表示每段数据后都冲,‘auto’表示轮询/等待一段长度为‘flushwait’毫秒的时间后如果没有输入便冲掉。目前该参数仅对AJP有效。
flushwait 10 表示如果‘flushpackets’为‘auto’,在冲掉输出组前等待额外输入的时间,单位毫秒。
keepalive Off 该参数应该在你的Apache和后端服务器之间有防火墙的时候时候,因为防火墙有可能会丢弃不活动的链接。该标志将会告诉操作系统在不活动的链接中发送KEEP_ALIVE消息(时间间隔由全局的OS设置决定,一般为120ms),这样避免防火墙丢弃该链接。要启用keepalive可以将该属性设置为On
lbset 0 对该工作单元所属的负载均衡集群进行设置。负载均衡器会先尝试较小的lbset,然后尝试大的。
ping 0 Ping数据告诉Web服务器在对ajp13链接递交请求前发送一个CPING请求。参数为等待CPONG响应的延迟,单位为秒。添加该功能是为了避免挂起和忙碌的Tomcat的一些问题。该功能需要ajp13 的ping/pong支持,已经在Tomcat 3.3.2+、4.1.28+、5.0.13+中实现。它会增加普通操作中的网络流量,有可能是个问题,但是它在集群节点失效或者繁忙的时候减少网络流量。目前该参数只对AJP有效。
loadfactor 1 工作单元负载系数。用于BalancerMember。它是一个1到100之间的数字,用于定义应用于该工作单元的正规化权重负载。
redirect 工作单元的重定向路由(Redirection Route)。该值通常被动态设置用于从集群中安全地移除某个节点。如果被设置了,所有不包含会话ID的请求都会被重定向到有等值route参数的BalancerMember中。
retry 60 线程池工作单元重试的超时时间,以秒为单位。如果到后端服务器的线程池工作单元状态是出错,Apache将不会递交任何请求到该服务器,直到超时结束。这可以令后端服务器关闭进行维护,并稍后上线。如果值为0则表示总是重试错误状态的工作单元不等待任何时间。
route 工作单元的路由,用于负载均衡器中。该路由是附加给会话ID的一个值。
status 用一个字符定义该工作单元的初始状态:‘D’表示禁用,‘S’表示已停止,‘I’表示忽略错误,‘H’表示热备,‘E’表示出错。状态可以通过前置‘+’表示设置或者‘-’表示清楚。这样,‘S-E’表示设置该工作单元为已停止并清除出错标志。
timeout Timeout 链接超时时间,单位为秒。如果未设置,那么Apache会一直等到有可用的链接位置。该指令常和max参数一起使用来限制到后端服务器的链接。
ttl 超出smax连接数的非活动链接的生存时间(Time To Live),单位为秒。Apache会关闭在这段时间内没有被用过的所有链接。

如果代理的指令类型以balancer://开头那么会创建一个虚拟工作单元,并不直接和后端服务器通讯。它负责对一些“真正”的工作单元进行管理。这种情况下可以给虚拟工作单元添加一系列额外参数。更多关于均衡器如何工作的信息请查看mod_proxy_balancer

参数 默认值 描述
lbmethod byrequests 选择均衡器的负载均衡方式。可以是byrequests,进行加权请求计数,或者是bytraffic,进行加权流量字节计数均衡。默认按请求数。
maxattempts 1 在放弃之前的故障转移的最大尝试次数。
nofailover Off 如果设为‘On’,当工作单元被禁用或者出错时,会话则立即中断。可以将该值设为On如果后端服务器不支持会话复制(Session replication)。
stickysession 均衡器粘连会话名称。该值常常会被设置为类似JSESSIONID或者PHPSESSIONID之类的值,他依赖于支持会话的后端应用服务器。如果后端服务器使用不同的cookie名称或者URL编码的ID(像servlet容器),使用|来分开他们。第一个部分针对cookie,第二个针对路径。
timeout 0 均衡器超时时间,单位为秒。如果设置了,那么就是等待空闲工作单元的最大时间。默认是不等。

均衡器设置范例

设置一个热备份,也就是只有当没有其他成员可用的时候才使用:

正常情况下,mod_proxy会标准化被ProxyPass的URL。但它可能会和某些后端不兼容,尤其是那些利用了PATH_INFO的。可选的nocanon关键词可以禁止它,并不动地直接将URL路径传递给后端。注意它可能会影响到后端的安全性,因为它跳过了由代理提供的对常见的基于URL的攻击的保护。

当用在<Location>节中的时候,第一个参数应被忽略,并且本地的路径将从<Location>取得。

如果你需要更加有弹性的反向代理配置,参见是用[P]标志的RewriteRule指令。

Windows上配置Code::Blocks + wxWidgets

27号晚上我问一个做共享软件的朋友Lazaru(基于FreePascal的跨平台IDE,类似于Delphi)做桌面软件如何,他推荐用Code::Blocks,说Nightly Build已经很稳定,正式版很快就发布了,接着果然28号就发布了正式版。

本文内容来自Code::Blocks wiki上的WxWindowsQuickRef,本文内容并非按照原文完全逐字逐句的翻译。

Code::Blocks是一个跨平台的C++IDE,支持Windows、Linux、MacOSX。同时他还支持各种不同的编译器,如GNU/MinGW C/C++,VC++ 6.0/2003/2005/2008,Borland C++,Digital Mars等等各种不同的编译器。

经过14个组员长达2年对Code::Blocks的全部重写,终于发布了正式版8.02,这个版本更包括了对构建基于wxWidgets的跨平台GUI程序的支持,堪比Visual C++。

wxWidgets则是一个十分优秀的跨平台的GUI框架,用其编写的C++应用程序可以十分方便地迁移到不同的系统上去。

Code::Blocks + wxWidgets两个同是支持跨平台的IDE和框架,使得跨平台的编程非常方便。然而Code::Blocks虽然包含了对wxWidgets的支持,但是却没有包含wxWidgets的构建环境,我们必须手动进行配置。另外,Code::Blocks有一个安装包包含了MinGW的编译器,如果使用别的编译器,同样也需要自己进行相应的配置。

前提准备

编译器

你至少应该正确安装了免费的MinGW/GCC编译器或者是某种微软的编译器Express editions是免费的,但是你还需要安装Platform SDK)。如果是用MinGW/GCC,至少要准备gcc-core、gcc-g++、binutils、w32api以及mingw32-make包;同时,确保包含编译器可执行文件的目录(一般是C:\MinGW\bin)在Windows的PATH环境变量中。

如果选择MinGW/GCC编译器,可以在直接选择包含MinGW的Code::Blocks安装包,见下一节。

最新版的Code::Blocks

请下载最新的8.02发布版。尚未选择编译器可以选择包含MinGW的安装包

wxWidgets

你可以选择下载wxWidgets的源代码然后自己进行构建,或者是直接安装预编译的wxPack。

wxWidgets源代码

安装包较小,可以根据自己的需求进行自定义构建,但是需要花费长时间进行编译。如果不清楚编译选项,可能导致无法成功配置Code::Blocks。

目前推荐的wxWidgets的版本是2.8.7。点击此处下载wxWidgets 2.8.7源代码Windows安装包 (wxMSW-2.8.7-Setup.exe; 12.0 MB)。你也可以检查一下wxWidgets的下载页面看看有没有更新的稳定版下载。强烈建议你将代码安装到不带空格的路径中。必须保证盘中至少有300MB的剩余空间。

wxPack

虽然安装包达200MB,全部安装需要3G,但是包含了预编译的所有可能用到的库文件,而且包含VC和GCC的两种版本,可以不用去考虑构建选项了。

当前wxPack的稳定发布版是 v2.8.7.03,基于 wxWidgets 2.8.7。点击此处下载 wxPack v2.8.7.03 (wxPack_v2.8.7.03.exe, 236.9 MB)。你也可以查看wxPack下载页面看看有没有更新的稳定版下载。强烈建议将wxPack安装到没有空格的路径中。如果你选择只MSVC版本,应保证至少有700MB的剩余空间;如果只选择MinGW/GCC版本,则应保证至少有2.2GB的剩余空间。

提示

如果磁盘使用了NTFS格式,可以开启文件压缩功能,上述的目录在压缩后可以减少50%的空间占用。

编译wxWidgets

使用wxPack则可以跳过这一步。

打开命令行(在开始菜单中点击“运行”,输入cmd并回车)。如果使用的MSVC,你可以使用特定的用于设置环境变量的命令行。如果你使用的MSVC版本还要求你单独下载Platform SDK,确保全部包含了标准编译工具和Platform SDK中要用到的环境变量。

转到wxWidgets的构建目录,其中<wxWidgets>是源码所在路径,通常是C:\wxWidgets-2.8.7

执行构建命令,MinGW/GCC推荐的命令是:

MSVC推荐的构建命令是:

这个过程需要花很久,快的机器大概30分钟可以完成,慢的可能就需要几个小时了。

如果使用的GCC的版本较新,构建过程中可能会出现大量的警告。这样会明显导致构建过程变慢;你可以将错误信息重定向到文件中,在上述命令后面添加2> err.log,也可以通过2>nul直接禁止警告信息。

其中关于BUILD、SHARED、MONOLITHIC以及UNICODE选项的解释,请仔细参考文章后面关于wxWidgets的构建参数的解释,这些参数十分关键,他们直接定义了你所使用的基本的wxWidgets开发环境。你必须严格按照你的编译参数设置Code::Blocks的配置向导。

在Code::Blocks中创建wxWidgets项目

在Code::Blocks的起始页面中,选择“Create a new project”,也可以在File菜单中,选择“New” -> “Project…”。

找到并选择“wxWidgets project”,并创建,接下来会出现一个向导帮助进行wxWidgets项目的配置:

  1. 第一个页面是简介,可以选择以后跳过。
  2. 选择你要使用的wxWidgets版本。如果你是按照本文的过程配置的,那么你应该选择“wxWidgets 2.8.x”。
  3. 设置你的项目的名字的位置。
  4. 输入作者的信息(非必要)
  5. 选择自动代码和文件生成的选项。
  6. 选择wxWidgets的位置。强烈建议在此使用全局变量:输入“$(#wx)”(不包含引号)。如果你还没定义这个全局变量,那么全局变量对话框会出现,在Base Path中,选择你的wxWidgets安装路径。其他路径可以不用填。
  7. 为你的项目选择debug/release配置。推荐至少选择debug配置。
  8. 选择你的wxWidgets构建选项。必须和你构建wxWidgets时所使用的选项一致!如果你按照本文之前的方式构建的,应该将“wxWidgets Library Settings”下的全部三个选项选中。如果用的是wxPack,由于wxPack包含了各种不同的版本,所以你只需要选择你需要的选项。这个页面的另一个设置和wxWidgets的构建选项没有关系,你可以按照喜好来选择。如果,出于某种原因,你想使用调试版本的wxWidgets构建,选择“Configure Advanced options”然后在下一页选择“Use __WXDEBUG__ and Debug wxWidgets lib”。
  9. 如果需要,选择额外的库。一般应用的话应该无须选择其中任何一个。

构建并运行程序

接下来,就可以选择“Build and run”(F9)对程序进行构建并运行了。如果顺利,你的wxWidgets应用程序就会出现。如果出现了什么问题,你可以参考后面的常见问题。

wxWidgets编译选项简介

BUILD

BUILD控制wxWidgets构建调试版本(BUILD=debug)或者是发布版本(BUILD=release)。绝大多数情况下你只需要wxWidgets的发布版本就可以了,因为你应该不想要去调试wxWidgets自身,同时你依然可以通过链接wxWidgets的发布版本来构建你自己的程序的调试版本。

  • 调试构建wxWidgets会创建带有”d”后缀的库,例如”libwxmsw28d.a”、”wxmsw28d_gcc_custom.dll”。
  • 调试构建wxWidgets会在wxWidgets库的输出目录中创建”mswd” 或者 “mswud” 目录。
  • 发布构建wxWidgets创建的库没有”d”后缀,例如”libwxmsw28.a”、”wxmsw28_gcc_custom.dll”。
  • 发布构建wxWidgets会在wxWidgets库的输出目录中创建”msw” 或者 “mswu” 目录。

SHARED

SHARED控制wxWidgets是构建DLL(SHARED=1)还是静态库(SHARED=0)。利用构建的DLL,主程序构建时间较快,可执行文件更小。但是可执行文件加上wxWidgets DLL的总大小更大,但是不同的可执行文件可以使用同一个DLL。

  • wxWidgets的DLL构建会创建导入库(如 libwxmsw28.a)以及DLL文件(如wxmsw28_gcc_custom.dll)。你必须在发布你的程序的时候包含这个DLL。
  • wxWidgets的静态构建只会创建静态库(如 libwxmsw28.a),发布的时候也无须包含wxWidgets的DLL。

MONOLITHIC

MONOLITHIC控制是构建一个单一的库(MONOLITHIC=1)还是多个组件库(MONOLITHIC=0)。使用单一构建,项目的设置和开发会更加简单,如果你同时使用DLL构建的话,你只需要分发一个DLL文件。如果使用非单一构建(multilib),会构建出多个不同的库同时你可以避免将整个wxWidgets的基本代码链接到主程序,就可以去掉不需要的库。同时你也必须确保你选择了正确的组件库。

  • wxWidgets的单一构建仅会创建一个wxWidgets导入库(如libwxmsw28.a)以及一个DLL(如wxmsw28_gcc_custom.dll)。
  • wxWidgets的多库(multilib)构建会创建多个导入库(libwx28_base.a等)以及多个DLL文件。
  • 无论何种wxWidgets构建,都会创建额外的静态库(如libwxexpat.a、libwxjpeg.a等)。这些库对于wxWidgets的DLL构建一般是不需要的,但是当使用静态构建的时候,则是必须的。

UNICODE

UNICODE控制wxWidgets以及你的程序是否使用支持Unicode的宽字符串。大多数Windows 2000或更高系统上的应用程序都应该支持Unicode。早期的Windows版本不一定有Unicode支持。你应该总是使用wxWidgets的_("string")_T("string")宏来确保硬编码的字符串编译时是正确的类型。

  • wxWidgets的Unicode(UNICODE=1)构建将会创建带有”u”后缀的库,例如”libwxmsw28u.a”、”wxmsw28u_gcc_custom.dll”。
  • wxWidgets的Unicode构建会在wxWidgets库的输出目录中创建”mswu”或”mswud”目录。
  • wxWidgets的ANSI(UNICODE=0)构建创建的库没有”u”后缀,例如”libwxmsw28.a”、”wxmsw28_gcc_custom.dll”。
  • wxWidgets的ANSI构建会在wxWidgets库的输出目录中创建”msw”或”mswd”目录。

常见问题

出现类似于”wx/setup.h: No such file or directory”的错误

你在构建选项中缺少了很重要的编译器搜索路径。首先确认你是否在运行wxWidgets项目向导的时候正确选择了wxWidgets的构建配置。如果重新运行向导并配置依然无效,那么打开你的项目的构建选项并给编译起的搜索路径中添加”$(#wx.lib)\gcc_dll\mswu“(这里假设是一个单一的Unicode DLL构建)。

出现类似于”cannot find -lwxmsw28u”的错误

构建选项中的链接库错了。首先确认你是否在运行wxWidgets项目向导的时候正确选择了wxWidgets的构建配置。如果重新运行向导并配置依然无效,确定你构建了什么库,并相应在构建选项中调整库的名字。

函数式JavaScript编程指南

函数式JavaScript编程指南

简介

你是否知道JavaScript其实也是一个函数式编程语言呢?本指南将教你如何利用JavaScript的函数式特性。

要求:你应当已经对JavaScript和DOM有了一个基本的了解。

写这篇指南的目的是因为关于JavaScript编程的资料太多了但是极少的资料提到了JavaScript的函数式特性。在本指南中,我只会讲解这些基本知识而不会深入其它的函数式语言或这是Lambda算子。

你可以点击所有的例子然后你所看到的代码就会被执行,这样就可以令指南变得具有交互性。你也可以使用这个沙箱来尝试。

第一课 —— 匿名函数

我们将首先介绍匿名函数。一个匿名函数就是一个没有名字的函数。
你可以认为他们是一次性函数。当你只需要用一次某个函数式,他们就特别有用。通过使用匿名函数,没有必要把函数一直放在内存中,所以使用匿名函数更加有效率。

例Example:

下面两个函数处理同样的事情,而average在给z赋值结束之后一直保留——但匿名函数则不会。

这很自然得引出了我们下面的一节课函数作为值

第二课 – 函数作为值

事实上,我们一般在JavaScript中声明函数的方式可以看作是一个简化了的语法(也就是语法糖syntactic sugar)。

例:

下面两个表达式其实完全一样。所以左边的表达式仅仅是右边的简写。

从这里可以得出一个结论,函数是一个值就像字符串、数字或数组一样。这还出现几个问题:

我是否可以把函数作为参数传递?
可以,见下面的例子。
是否可以实时生成函数?
当然了,这是一个高级的主题,它可以通过eval函数来完成。小提示:看看本页面的源代码。

例:

这个例子演示了如何把函数作为参数传递。

第三课 – 两种方式调用函数

在JavaScript中,有两种调用函数的方式。一般的方式是把参数放在括号中,如alert(42)。另一种方式是同时把函数和参数都放在括号中,如(alert)(42)

例:

为什么函数两边的括号很重要:如果你写了括号,那么在括号中的代码就会被先计算。在计算之后,括号所在的地方就会有一个值。这个值可能是一个字符串、一个数字或一个函数。

第四课 – “短路”条件调用

现在我们将学习如何使用“短路”条件调用。使用这个方法可以缩短源代码同时代码也变得更加可读。

例:

这个语法并不是用在左表达式上,而是用在右表达式上。

第五课 – 它好在哪里

OK,现在我们已经学习了一些函数式JavaScript的内容。那么它好在哪里?函数式JavaScript编程之所以很重要有三条主要的理由:

  1. 它有助于写出模块化和可服用的代码。
  2. 它对事件处理程序非常有效。
  3. 它很有趣!

在下面的篇幅中,我会给出更多关于前两条理由的信息

1. 模块化和可复用的代码

现在你已经知道如何将函数作为值使用,那么你也应该试试!一个很好的例子是数组内建的sort方法。预定义的sort()把所有的对象转换成字符串并把他们按照词语的顺序排序。但如果我们有用户自定义的对象或者数字那么它就不是很有用了。于是这个函数可以让你给他一个进行比较的函数作为参数,如sort(compareFunction)。这个方法让我们甚至不用接触实际的sort方法。

例:

2. 事件处理程序

对事件处理程序使用函数式编程也许是最直观的函数作为值得应用了。既然这样我们马上就演示一个例子。

简单的例子:;ie

现在有一个Button类,带一个自定义的onclick行为。

练习: 为什么我们要把alert包裹在一个匿名函数中?

高级例子:

现在我们想改进我们的Button类。每一个按钮都被分配了一个值当按钮被点击时显示该值。首先我们调整我们的类:

下面你也许要尝试写下面的代码:

如果你执行它你就会发现提示框中间是空的。为什么会这样呢?其实原因在于JavaScript的可见性规则。当onclick函数被执行时this指向的是按钮的DOM节点而非自定义的按钮对象。

我们如何解决这个问题? 使用函数式编程:

这种情况下执行该匿名函数会将v绑定到this.value上。

沙箱

在此处输入你的代码,并按下计算.


更多信息

下面是关于函数式JavaScript编程的一些有趣的链接:

展望

本节给大家展示一下JavaScript的未来。一个非常振奋人心的JavaScript特性——E4X,一个JavaScript中直接的XML支持。

TCMalloc:线程缓存的Malloc

作者:Sanjay Ghemawat, Paul Menage

原文

翻译:ShiningRay

动机

TCMalloc要比glibc 2.3的malloc(可以从一个叫作ptmalloc2的独立库获得)和其他我测试过的malloc都快。ptmalloc在一台2.8GHz的P4机器上(对于小对象)执行一次mallocfree大约需要300纳秒。而TCMalloc的版本同样的操作大约只需要50纳秒。malloc版本的速度是至关重要的,因为如果malloc不够快,应用程序的作者就很有可能在malloc之上写一个自己的自由列表。这就可能导致额外的代码复杂度,以及更多的内存占用――除非作者本身非常仔细地划分自由列表的大小并经常从自由列表中清除空闲的对象。

TCMalloc也减少了多线程程序中的锁争用情况。对于小对象,几乎已经达到了零争用。对于大对象,TCMalloc尝试使用粒度较好和有效的自旋锁。ptmalloc同样是通过使用每线程各自的场地来减少锁争用,但是ptmalloc2使用每线程场地有一个很大的问题。在ptmalloc2中,内存可能会从一个场地移动到另一个。这有可能导致大量空间被浪费。例如,在一个Google的应用中,第一阶段可能会为其URL标准化的数据结构分配大约300MB内存。当第一阶段结束后,第二阶段将从同样的地址空间开始。如果第二个阶段被安排到了一个与第一阶段什?用的场地不同的场地,这个阶段不会复用任何第一阶段留下的的内存,并会给地址空间添加另外一个300MB。类似的内存爆炸问题也可以在其他的应用中看到。

TCMalloc的另一个好处是小对象的空间最优表现形式。例如,分配N个8字节对象可能要使用大约8N * 1.01字节的空间。即,多用百分之一的空间。而ptmalloc2中每个对象都使用了一个四字节的头,(我认为)并将最终的尺寸规整为8字节的倍数,最后使用了16N字节。

使用

要使用TCMalloc,只要将tcmalloc通过“-ltcmalloc”链接器标志接入你的应用即可。

你也可以通过使用LD_PRELOAD在不是你自己编译的应用中使用tcmalloc:

LD_PRELOAD比较讨巧,我们也不十分推荐这种用法。

TCMalloc还包含了一个堆检查器以及一个堆测量器

如果你更想链接不包含堆测量器和检查器的TCMalloc版本(比如可能为了减少静态二进制文件的大小),你可以接入libtcmalloc_minimal

概览

TCMalloc给每个线程分配了一个线程局部缓存。小分配可以直接由线程局部缓存来满足。需要的话,会将对象从中央数据结构移动到线程局部缓存中,同时定期的垃圾收集将用于把内存从线程局部缓存迁移回中央数据结构中。

overview

TCMalloc将尺寸小于<=
32K的对象(“小”对象)和大对象区分开来。大对象直接使用页级分配器(一个页是一个4K的对齐内存区域)从中央堆直接分配。即,一个大对象总是页对齐的并占据了整数个数的页。

连续的一些页面可以被分割为一系列小对象,而他们的大小都相同。例如,一个连续的页面(4K)可以被划分为32个128字节的对象。

小对象的分配

每个小对象的大小都会被映射到170个可分配的尺寸类别中的一个。例如,在分配961到1024字节时,都会归整为1024字节。尺寸类别这样隔开:较小的尺寸相差8字节,较大的尺寸相差16字节,再大一点的尺寸差32字节,如此类推。最大的间隔(对于尺寸 >= ~2K的)是256字节。

一个线程缓存对每个尺寸类都包含了一个自由对象的单向链表

thread heap

当分配一个小对象时:

  1. 我们将其大小映射到对应的尺寸类中。
  2. 查找当前线程的线程缓存中相应的自由列表。
  3. 如果自由列表不空,那么从移除列表的第一个对象并返回它。当按照这个快速通道时,TCMalloc不会获取任何锁。这就可以极大提高分配的速度,因为锁/解锁操作在一个2.8GHz Xeon上大约需要100纳秒的时间。

如果自由列表为空:

  1. 从该尺寸类别的中央自由列表(中央自由列表是被所有线程共享的)取得一连串对象。
  2. 将他们放入线程局部的自由列表。
  3. 将新获取的对象中的一个返回给应用程序。

如果中央自由列表也为空:(1) 我们从中央页分配器分配了一连串页面。(2) 将他们分割成该尺寸类的一系列对象。(4) 像前面一样,将部分对象移入线程局部的自由列表中。

大对象的分配

一个大对象的尺寸(> 32K)会被除以一个页面尺寸(4K)并取整(大于结果的最小整数),同时是由中央页面堆来处理的。中央页面堆又是一个自由列表的阵列。对于i < 256而言,第k个条目是一个由k个页面组成的自由列表。第256个条目则是一个包含了长度>= 256个页面的自由列表:

Page heap

k个页面的一次分配通过在第k个自由列表中查找来完成。如果该自由列表为空,那么我们则在下一个自由列表中查找,如此继续。最终,如果必要的话,我们将在最后一个自由列表中查找。如果这个动作也失败了,我们将向系统获取内存(使用sbrkmmap或者通过在/dev/mem中进行映射)。

如果k个页面的一次分配行为由连续的长度> k的页面满足了,剩下的连续页面将被重新插回到页面堆的对应的自由列表中。

跨度(Span)

TCMalloc管理的堆由一系列页面组成。连续的页面由一个“跨度”(Span)对象来表示。一个跨度可以是已被分配或者是自由的。如果是自由的,跨度则会是一个页面堆链表中的一个条目。如果已被分配,它会是一个已经被传递给应用程序的大对象,或者是一个已经被分割成一系列小对象的一个页面。如果是被分割成小对象的,对象的尺寸类别会被记录在跨度中。

由页面号索引的中央数组可以用于找到某个页面所属的跨度。例如,下面的跨度a占据了2个页面,跨度b占据了1个页面,跨度c占据了5个页面最后跨度d占据了3个页面。

在一个32位的地址空间中,中央阵列由一个2层的基数树来表示,其中根包含了32个条目,每个叶包含了 215个条目(一个32为地址空间包含了 220个 4K 页面,所以这里树的第一层则是用25整除220个页面)。这就导致了中央阵列的初始内存使用需要128KB空间(215*4字节),看上去还是可以接受的。

在64位机器上,我们将使用一个3层的基数树。

解除分配

当一个对象被解除分配时,我们先计算他的页面号并在中央阵列中查找对应的跨度对象。该跨度会告诉我们该对象是大是小,如果它是小对象的话尺寸类别是什么。如果是小对象的话,我们将其插入到当前线程的线程缓存中对应的自由列表中。如果线程缓存现在超过了某个预定的大小(默认为2MB),我们便运行垃圾收集器将未使用的对象从线程缓存中移入中央自由列表。

如果该对象是大对象的话,跨度会告诉我们该对象覆盖的页面的范围。假设该范围是[p,q]。我们还会查找页面p-1和页面q+1对应的跨度。如果这两个相邻的跨度中有任何一个是自由的,我们将他们和[p,q]的跨度接合起来。最后跨度会被插入到页面堆中合适的自由列表中。

小对象的中央自由列表

就像前面提过的一样,我们为每一个尺寸类别设置了一个中央自由列表。每个中央自由列表由两层数据结构来组成:一系列跨度和每个跨度一个自由对象的链表。

通过从某个跨度中移除第一个条目来从中央自由列表分配一个对象。(如果所有的跨度里只有空链表,那么首先从中央页面堆中分配一个尺寸合适的跨度。)

一个对象可以通过将其添加到他包含的跨度的链表中来返回到中央自由列表中。如果链表长度现在等于跨度中所有小对象的数量,那么该跨度就是完全自由的了,就会被返回到页面堆中。

线程缓存的垃圾收集

某个线程缓存当缓存中所有对象的总共大小超过2MB的时候,会对他进行垃圾收集。垃圾收集阈值会自动根据线程数量的增加而减少,这样就不会因为程序有大量线程而过度浪费内存。

我们会遍历缓存中所有的自由列表并且将一定数量的对象从自由列表移到对于得中央列表中。

从某个自由列表中移除的对象的数量是通过使用一个每列表的低水位线L来确定的。L记录了自上一次垃圾收集以来列表最短的长度。注意,在上一次的垃圾收集中我们可能只是将列表缩短了L个对象而没有对中央列表进行任何额外访问。我们利用这个过去的历史作为对未来访问的预测器并将L/2个对象从线程缓存自由列表中移到相应的中央自由列表中。这个算法有个很好的特性是,如果某个线程不再使用某个特定的尺寸时,该尺寸的所有对象都会很快从线程缓存被移到中央自由列表,然后可以被其他缓存利用。

性能备注

PTMalloc2单元测试

PTMalloc2包(现在已经是glibc的一部分了)包含了一个单元测试程序t-test1.c。它会产生一定数量的线程并在每个线程中进行一系列分配和解除分配;线程之间没有任何通信除了在内存分配器中同步。

t-test1(放在tests/tcmalloc/中,编译为ptmalloc_unittest1)用一系列不同的线程数量(1~20)和最大分配尺寸(64B~32KB)运行。这些测试运行在一个2.4GHz 双核心Xeon的RedHat 9系统上,并启用了超线程技术, 使用了Linux glibc-2.3.2,每个测试中进行一百万次操作。在每个案例中,一次正常运行,一次使用LD_PRELOAD=libtcmalloc.so

下面的图像显示了TCMalloc对比PTMalloc2在不同的衡量指标下的性能。首先,现实每秒全部操作(百万)以及最大分配尺寸,针对不同数量的线程。用来生产这些图像的原始数据(time工具的输出)可以在t-test1.times.txt中找到。

  • TCMalloc要比PTMalloc2更具有一致地伸缩性——对于所有线程数量>1的测试,小分配达到了约7~9百万操作每秒,大分配降到了约2百万操作每秒。单线程的案例则明显是要被剔除的,因为他只能保持单个处理器繁忙因此只能获得较少的每秒操作数。PTMalloc2在每秒操作数上有更高的方差——某些地方峰值可以在小分配上达到4百万操作每秒,而在大分配上降到了<1百万操作每秒。
  • TCMalloc在绝大多数情况下要比PTMalloc2快,并且特别是小分配上。线程间的争用在TCMalloc中问题不大。
  • TCMalloc的性能随着分配尺寸的增加而降低。这是因为每线程缓存当它达到了阈值(默认是2MB)的时候会被垃圾收集。对于更大的分配尺寸,在垃圾收集之前只能在缓存中存储更少的对象。
  • TCMalloc性能在约32K最大分配尺寸附件有一个明显的下降。这是因为在每线程缓存中的32K对象的最大尺寸;对于大于这个值得对象TCMalloc会从中央页面堆中进行分配。

下面,CPU时间的每秒操作数(百万)以及线程数量的图像,最大分配尺寸64B~128KB。

这次我们再一次看到TCMalloc要比PTMalloc2更连续也更高效。对于<32K的最大分配尺寸,TCMalloc在大线程数的情况下典型地达到了CPU时间每秒约0.5~1百万操作,同时PTMalloc通常达到了CPU时间每秒约0.5~1百万,还有很多情况下要比这个数字小很多。在32K最大分配尺寸之上,TCMalloc下降到了每CPU时间秒1~1.5百万操作,同时PTMalloc对于大线程数降到几乎只有零(也就是,使用PTMalloc,在高度多线程的情况下,很多CPU时间被浪费在轮流等待锁定上了)。

注意

对于某些系统,TCMalloc可能无法与没有链接libpthread.so(或者你的系统上同等的东西)的应用程序正常工作。它应该能正常工作于使用glibc 2.3的Linux上,但是其他OS/libc的组合方式尚未经过任何测试。

TCMalloc可能要比其他malloc版本在某种程度上更吃内存,(但是倾向于不会有其他malloc版本中可能出现的爆发性增长。)尤其是在启动时TCMalloc会分配大约240KB的内部内存。

不要试图将TCMalloc载入到一个运行中的二进制程序中(例如,在Java中使用JNI)。二进制程序已经使用系统malloc分配了一些对象,并会尝试将它们传递到TCMalloc进行解除分配。TCMalloc是无法处理这种对象的。

什么是 1% 规则?

作者:Charles ArthurThe Guardian
翻译:ShiningRay

一个正在浮现的规则告诉我们——如果你的网站有100个人(一组)的在线量,只有1个会创建新的内容,有 10 个会与之进行交互(回复或者提供改进) ,剩下的89个就仅仅浏览一下。

在YouTube的统计数据中也显示出了几乎一样的情况。YouTube在18个月内就从零开始,占据了现在所有在线视频浏览的60%的份额。

数据显示出:每天有10亿次下载以及6,5000次上传——正如 Antony Mayfield所指出的那样,每次上传有1,538次下载——每个独立用户每月20m。

这个数据中的“创作者对消费者”的比率只有0.5%,不过是现在下最终解决还比较早,因为还不是所有人都发现了YouTube(还有就是下载比上传要方便得多,因为任何网页都能包含YouTube的链接)。

再思考一些其他的依赖社区产生内容的项目的统计数据,Wikipedia:Wikipedia所有文章的50%的编辑工作是由0.7%的用户来完成的,而有超过70%的文章是由1.8%的用户来撰写的。该数据来自Church of the Customer的Blog

从一些社区网站收集的早期的数据显示大约80%的内容是由20%的用户产生的,但是不断增长的数据量给出了清晰的图景,告诉我们Web 2.0的网站应该如何去思考。例如,一个网站如果是要求大量的用户交互和从用户创建的内容的话,它会发现10个人中有9个只是路过而已。

Yahoo的Bradley Horowitz指出相同的情况也适用于Yahoo:在Yahoo Groups(Yahoo的讨论组)上,“用户人口中的1%可能会创建一个讨论组;10%的用户可能会参与地比较活跃,同时实际创作新的内容——开启一个新的主题或者回复已有的主题;100%的用户可以从前面的用户的活动中受益,”他于二月份在其Blog上谈论了这一点

那么结论是什么?就是你不能对在线的用户期待太多。。问题在于——和现实生活一样——是如何找到构建者。

Zend Framework 指南

作者:Chris Shiflett
翻译:ShiningRay

我们邀请了PHP安全专家——兼最新发布的Zend Framework的贡献者——Chris Shiflett来为我们写一篇关于ZF主要特点的文章。

这份完整的、按部就班的教程通过向你展示如何应用框架写出一个简单的新闻管理系统,为你提供了构建实际应用的独特视角。

Zend Framework终于掀开了其神秘的面纱!尽管它尚处于开发过程的早期阶段,但本文将现在所能用的中最好的部分特别呈现给读者,并通过构建一个简单的应用这个过程引导你了解这个框架。

Zend很早就发布了框架并引入社区运作。写本指南只能针对框架今天的情况来列出其特点。因为本指南是在线发布的,所以我会在框架发生变化的时候及时更新本文,这样就能尽可能保持一致。

要求

Zend Framework 要求使用 PHP 5。为了能完全利用本指南中展示的代码,你还需要Apache Web服务器,因为范例应用(一个新闻管理系统)用到了mod_rewrite。

本指南中的代码可以自由下载,所以你可以亲自尝试一下。可以从Brain Bulb的网站上下载到:http://brainbulb.com/zend-framework-tutorial.tar.gz.

下载框架

在开始阅读本指南之前,你还需要先下载一份框架的预览发布版。可以用浏览器察看 http://framework.zend.com/download 并选择 tar.gz 或者 zip 文件手工下载,也可以使用下面的命令行:

清单1

注意

Zend已经计划提供一个独立的PEAR通道来方便下载。

下载了预览发布版之后,将这个库的目录放到一个方便的地方。在本教程中,我将库的目录名改称了lib,以便提供一个简单干净的目录结构:

清单2

www 目录是文档根目录,controllers和views目录为空,是将来要用的,lib目录是来自于下载的预览版。

入门

我第一个要向你展示的组件是Zend_Controller。在很多情况下,它为要开发的应用提供了一个基础,同时它也是令Zend Framework超越了组件集合的一个部分。不过在使用它之前,需要将所有进入的请求引导到某个PHP脚本中。本教程将使用mod_rewrite来完成这个目的。

如何用好mod_rewrite确实是一门艺术,不过还好本文中这个特殊的任务十分简单。如果你对mod_rewrite或者是Apache的一般配置还不是很熟悉的话,可以在文档根目录下创建一个.htaccess文件,并加入一下指令:

清单3

Zend_Controller目前的任务之一就是去掉对mod_rewrite的依赖。为了提供一个预览版可以使用的例子,本教程便使用了mod_rewrite。

如果你直接在httpd.conf中添加这些指令,还必须重新启动Web服务。但是,如果使用.htaccess文件,就不用了,而且这样更好。你可以随便在index.php中写点东西,然后任意请求某些路径来测试一下,比如/foo/bar。例如,如果你的主机是example.org,那么就请求URL http://example.org/foo/bar。

可能你还想在 include_path 中包含框架库的路径。你可以直接在php.ini中配置,或者可以将以下指令放入.htaccess文件中:

清单4

Zend

Zend 类包含了一系列十分通用也十分有用的静态方法。这是唯一一个需要手工进行包含的类:

清单5

一旦引用了Zend.php,就可以访问Zend类中的所有方法了。使用loadClass()方法,载入其他类也变得简单了。例如载入Zend_Controller_Front类:

清单6

loadClass()方法会考虑到include_path,同时它还知道框架的目录组织结构。我就使用它来载入所有其他的类。

Zend_Controller

这个控制器的使用还是比较直观的。实际上,我写这份指南的时候可没有官方文档可用!

现在ZF网站上已经有官方文档了。

首先讲Zend_Controller_Front,这是一个前端控制器。你可以将下面的代码放入index.php中来理解它是如何工作的:

清单7

如果你更希望使用对象链方式,可以如下改写:

清单8

现在,当进行/foo/bar的请求时,就会出现一个错误。这很好!它至少能告诉你有动作了。主要的问题是未发现IndexController.php

在创建这个文件之前,最好首先了解框架组织东西的方式。框架会将一个请求分解成几个部分,在这个例子中,请求/foo/bar,foo是控制器,bar是动作。两者的默认值都是index。

当foo作为控制器时,框架首先在控制器目录中查找名叫FooController.php的文件。因为不存在这个文件,于是框架退一步查找IndexController.php。如果还是没有发现,就报告错误。

下面,在controllers目录(可以使用setControllerDirectory()自己设置)中创建IndexController.php

清单9

IndexController类处理控制器为index的请求或者指定的控制器不存在的请求,就像刚才所说的。indexAction()方法将处理动作为index的请求。记住无论是控制器还是动作,默认的值都是index,前面也说过了。如果尝试请求/、/index或者/index/index,都会执行indexAction()方法(结尾的斜杠不会改变这种行为)。对任何其他资源的请求都可能会产生错误。

继续之前还要为IndexController添加一个很有用的方法noRouteAction()。一旦请求某个控制器且其不存在时便调用noRouteAction()方法。例如,请求/foo/bar时,如果FooController.php不存在,那么就会执行noRouteAction()。不过,/index/foo的请求还是会产生错误,因为这里foo是一个动作,而非控制器。

在IndexController中添加 noRouteAction() :

清单10

这个例子使用了 $this->_redirect(‘/’) 来描述可以在noRouteAction()中一般可能出现的动作。这可令对不存在的控制器的请求被重定向到根文档(首页)。

现在来创建 FooController.php:

清单11

如果你再请求 /foo/bar,就应该看到执行了barAction(),因为请求的动作是bar。这样不仅可以支持友好的URL,同时也可以用极少的代码就可以很好得进行组织。酷啊!

还可以创建一个__call()方法来处理未定义的动作的请求,如 /foo/baz:

清单12

现在只需几行代码就可以很优雅地处理进入的请求了,让我们继续。

Zend_View

Zend_View是一个可以协助你组织视图逻辑的类。它并不使用特定的模版系统,为了简便起见,在本例中我也不会使用模版系统。然而,你可以随意使用你喜欢的。

记住所有进入的请求都是由前端控制器来处理的。因此,既然应用程序的框架已经这样存在了,那么以后的天价都必须适应它。为了演示Zend_View最基本的用法,我们将IndexController.php中的代码修改成这样:

清单13

在视图目录(这里是views)创建一个叫做example.php的文件:

清单14 –

现在,当请求网站的根的资源时,应该可以看到example.php的内容。虽然现在这还不是很有用,不过记住你的工作目标是按照一个结构化、有组织的方法来开发Web应用。

为了更清楚地利用Zend_View,将模版(example.php)修改一下,包含一些数据:

清单15

添加了两个额外的特性。$this->escape()方法必须用在所有的输出上。即便你要自己创建输出(例如这个例子中的),也要将所有的输出进行转义,这种好习惯可以防止出现跨站脚本(XSS)。

$this->title$this->body特性在这里是用于演示动态数据的。它们应该在控制器中定义,所以让我们修改IndexController.php来给他们赋值:

清单16

现在你再浏览网站,应该看到了模版所使用的这些值。在模版中使用$this的原因是模版是在Zend_View的实例的范围内执行的。

记住example.php仅仅是一个普通的PHP脚本,所以你可以在其中做任意你想做的事情。最好还是尽量遵守规则,仅仅在模版中进行显示数据的工作。控制器(或者是控制器进行分配的模块)则应该承担所有的业务逻辑。

在继续讲之前,我要最后对Zend_View做一个补充。在每个控制器的方法中实例化$view对象会要额外输入很多东西,而我们的主要目标是更简单、更快速地进行开发。而且,如果模版都存放在同一个目录下的话,每次都要调用setScriptPath()也是一件麻烦事。

幸好,Zend类包含了一个注册表,它可以帮助我们消除这种反复的工作。你可以使用register()方法将$view对象存放在注册表中:

清单17

可以使用registry()方法来获取它:

清单18

从今往后,本教程就会使用注册表了。

Zend_InputFilter

The last component this tutorial covers is Zend_InputFilter. This class provides a simple but rigid approach to input filtering. You instantiate it by passing an array of data to be filtered:

清单19

他会把数组($_POST)设置为NULL,这样就不可能再直接进行访问了。Zend_InputFilter提供了一些根据特定条件筛选数据的方法。例如,如果你需要$_POST['name']都是字母,可以使用getAlpha()方法:

清单20

传给每一个筛选方法的参数是对应要进行筛选的数组元素的键。该对象(在这个例子中是$filterPost)是一个包含了有错误的数据的保护笼,这使得对数据的访问更加可控和一致。因此,当要访问输入数据的时候,应该使用Zend_InputFilter

Zend_Filter提供了一些静态的过滤方法,这些方法和Zend_InputFilter的方法遵循同样的规则。

创建一个新闻管理系统

尽管预览版还包含了很多组件(甚至还有更多的正在进行开发),但是只需要使用前面讨论过的组件就可以构建简单的应用了。在构建应用的过程中,你就会对框架的基本结构和设计有一个更加清晰的理解。

每个人开发应用的方式都有所不同,所以Zend Framework尽可能地尝试包容这些差异。同样,本教程是根据我的偏好写的,所以你可以将它们调整为适合自己口味的方式。

当我开始开发某个应用时,我是先从界面开始的。这并不是说我喜欢做表面文章,花大量功夫在样式、图片上,其实我是站在一个用户的角度来透视问题。这样,我将一个应用看作是一系列页面的集合,每个页面对应一个唯一的URL。这个新闻管理系统有以下一些URL组成:

清单21

马上开始根据控制器考虑这些URL。IndexController显示新闻、AddController处理新闻和评论的添加,AdminController处理诸如审批新闻之类的管理工作,以及ViewController用于查看指定的新闻条目和相应的评论。

首先,如果有FooController.php,先将其删除,并修改IndexController.php并添加合适的动作,并加入一些关于业务逻辑的注释放着:

清单22

下面,创建AddController.php:

清单23

注意AddControllerindexAction()不应被调用。只有当请求的路径是/add时才会调用这个方法。因为用户也许会手工输入URL,还是有可能会被调用的,所以这时应该将用户重定向到首页、显示一个错误或者你觉得合适的动作。

下面,创建AdminController.php:

清单24 –

最后,创建ViewController.php:

清单25

AddController中的一样,index()方法应该是不可能会被调用的,所以可以在其中安排任何动作。ViewController和其他的有些不同,因为还不知道什么是有效的动作。为了能支持类似/view/23这样的URL,必须使用__call()来支持动态动作。

与数据库进行交互

因为Zend Framework的数据库组件相对还不太稳定,同时我又希望例子更容易使用,所以我使用了SQLite的类来存储和获取新闻条目以及评论:

清单26

当然,你可以用自己的解决方案来替换这个类。包含它只为了提供一个完整的例子,并不是给出一个推荐的实现。

这个类的构造器需要获得SQLite数据库的完整路径和文件名,数据库必须事先创建好:

清单27

这只需要运行一次,然后就只要将完整的路径和文件名传给Database类就行了:

清单28

整合

为了能将所有的东西放到一起,先在lib目录中创建Database.php,这样loadClass()就能找到它了。而index.php文件现在要实例化$view$db并将它们存储在注册表中。也可以创建一个叫做__autoload()的函数来自动加载所有需要用到的类:

清单29

下面,在视图目录中创建一些简单的模版。index.php文件可以用于显示index视图:

清单30 –

view.php 模版可以用于显示特定的新闻条目:

清单31

最后,admin.php模版可以用于审批新闻条目:

清单32

为了简单起见,就使用一个带密码的表单作为访问控制机制。

放好了这些模版之后,就只要将原来放在控制器中占着位置的注释替换成几行代码。例如,IndexController.php就变成了:

清单33

组织好所有的东西之后,应用的首页的完整的业务逻辑就被减至四行代码。AddController.php还要再处理一下,需要更多的代码:

清单34

因为用户在提交了一个表单之后被重定向了,所以在这个控制器里面不需要视图。

AdminController.php中,要处理两个动作,显示管理界面和审批新闻:

清单35

最后是ViewController.php:

清单36

这段代码提供了一个功能完整——虽然非常简单的——的新闻共享和评论的应用。最好的地方就是借助于框架优雅的设计,添加更多的功能十分简单,同时随着Zend Framework的逐渐程序,各方面都会变得更好。

更多资料

本指南仅仅是讲解了一些皮毛,还有一些其他的资源可以参考。除了官方手册之外,Rob Allen提供了一些他对于Zend Framework的详细感受,Richard Thomas也给出了一些有用的注释。如果你有自己的看法,可以在本页上留下留言说出自己的想法。

结语

不过还是能发现这个预览版有些粗糙,我自己写这个教程的时候,遇到过一些挫折。不过整体上来说,我认为Zend Framework兑现了大部分承诺,同时所有的参数者依然在继续努力改进它。Chris Shiflett 是 Brain Bulb——一个专于PHP开发和安全性的咨询公司—— 的主席,你可以阅读一下Chris的Blog或者访问Brain Bulb的网站

Ma client server简明教程

翻译:ShiningRay @ NirvanaStudio
原文地址:http://minnow.cc.gatech.edu/squeak/2978

{译者:Ma client server是针对Smalltalk的一个网络应用框架,由于十分好用,介绍到这里来}

简介

Ma client server 可以帮助你非常方便地书写客户端/服务器端(Client/Server,以下简称C/S)程序。事实上,它容易到了服务器段程序只要用一行代码:

MaServerSocket new listenOn: 12345 answer: [ :requestByteArray | 'Hello world!' asByteArray ]

这个框架实际上是基于一个稳定可靠的、多线程的Socket模型,是由John McIntosh分享给我的。

在C/S模型中,一个客户端向服务器段发送一个请求,然后服务器处理请求并返回一个应答。

这个框架提供了相互配合的两层结构。你选择其中你想使用的一层然后写两个程序。一个客户端程序会给远程服务器发送请求,服务器上则运行着另一个程序处理请求。

如果你选择较低的一层,“套接字层”(Socket Layer),你发送的是ByteArray(字节数组,一个基本类)的请求,接收的是ByteArray的应答。在较高一层,“对象请求层”(Object Rquest Layer),你可以发送任意的请求对象,获取任意的对象作为应答。较高层上使用了“Ma对象序列化”(Ma Object Serialization)框架来将对象转换成ByteArray,然后让较低的一层为你把这个ByteArray发送出去。

使用套接字层

建立服务器

如果你只是要发送和接收ByteArray的数据,你可以随便写你的服务器程序。但记住,不管在哪里,你要接收ByteArray作为输入,你就必须应答一个ByteArray

然后,要启动服务器:

myServerSocket _ MaServerSocket new
listenOn: 12345
answer:
[ :requestByteArray |
... 将 requestByteArray 传给你的服务器端程序 ... ]

MaServerSocket 利用了 SharedQueue和后台进程来确保处理过程不会因在网络上发送和接收ByteArray数据而阻塞,现在发送和接收完全在后台进行。“answer”块一次对一个客户端请求进行执行。

当你完成之后,一定要关闭套接字这样资源才能正确被释放:

myServerSocket shutdown

在客户端获得应答

下面客户端程序建立了一个MaClientSocket对象,要将IP地址作为ByteArray中的数字指明:

mySocket _ MaClientSocket hostAddress: #(192 168 1 1) asByteArray port: 12345.

建立了套接字之后还没有进行任何连接。只有在你发送请求的时候才会发生:

responseByteArray _ mySocket sendData: myRequestByteArray

同时具有一个sendData:startingAt:count:waitForReplyIn:方法可以达到最大的效率。如果你提供作为返回的ByteArray 不够大,你就会得到一个更大的。

可选的套接字事件

MaServerSocket 要用到一个 “server”, 默认是一个MaServer对象不过它什么也不做。但如果你给MaServer派生子类并且重写其中一些方法,这些方法会在如下时候调用套接字加入队列、一个ByteArray请求完全抵达,或者是发送一个响应等等。详细信息见MaServer

使用对象请求层

如果你选择使用对象请求层,你发送“请求”对象并获得某种应答对象。这样你可以专心写客户端和服务器程序,而不用关系如何解析ByteArray了。

建立服务器

这里当你准备开始写你的服务器段程序时,不是建立一个MaServerSocket对象,而是建立一个MaTcpRequestServer对象:

myServer _ MaTcpRequestServer protocol:
{ MyRequestClass1.
MyRequestClass2.
MyResponseClass1.
MyResponseClass2.
OrderedCollection.
Dictionary. "etc." }.

这是什么?是的,你必须告诉她你要发送和接收的所有类,来记录你请求对象的形式。例如,如果你有一个 SubmitArticleRequest类,里面有一个Article对象,你不仅要列出SubmitAritcleRequest类,还要给出Article和所有其他Article引用的类和这些类他们引用的类,如此继续。然而,你不需要给出String, ByteArray, Set, Symbol, SmallInteger, Float, True, False或者UndefinedObject等类。这些类已经自动成为协议的一部分了。

也可以指明一个MaServerConsole来接收事件。派生MaServerConole并改写他的一个通知方法,象下面这样,他就会被自动调用。

myServer _ console: myConsole

当你准备启动服务器时,你要使用 processOn:do:

myServer
processOn: 12345 ;
using:
[ :requestObject |
... 将 requestObject 传递给服务器程序,可以应答响应 ... ]

当完成之后,你需要关闭并释放系统资源:

myServer shutdown

建立客户端

你的客户端程序建立一个 MaTcpRequestServerLink 并指向服务器所监听的地址 hostAddress 和端口 port

myLinkToServer _ MaTcpRequestServerLink hostAddress: #(192 168 1 1) asByteArray port: 12345

在你要做任何事之前,你必须先建立连接。这确保你在客户端的镜像中包含了所有必须的协议 (如果没有就会抛出异常)。

myLinkToServer connect

如果要在网络状况和你的耐性做出平衡,你可能想改写默认的超时期限为30秒:

myLinkToServer timeoutSeconds: 30

在客户端获得应答

现在你已经连上了,你的客户端程序可以如下从服务器端获得应答:

myLinkToServer submit: someRequestObject

记住, someRequestObject 必须是协议的成员之一,同样你从submit:取回的应答对象也必须是协议之一:

好了!当你完成之后,别忘了断开连接这样你可以将网络资源还给系统

myLinkToServer disconnect

有趣的Morphic图形系统

教程: 有趣的Morphic图形系统

作者:John Maloney
翻译: @ NirvanaStudio
原文地址:http://static.squeak.org/tutorials/morphic-tutorial-1.html

这篇教程要用在Morphic工程中。要建立一个Morphic工程,只要在Squeak窗口的背景上点击鼠标,不要再任何其他窗口上,出现了屏幕菜单。选择“open….”并选择“project(mophic)”。一个小的桔色的“Unnamed”Morphic工程窗口就出现了。点击窗口中间可以放大到全屏幕。(如果你不确定你在什么地方,只要探出屏幕菜单——如果标题是“World”,那你已经在Morphic工程中了)

首先我们需要建立一个浏览器“Browser”和一个工作区“Workspace”。先复习一下,在Squeak屏幕的空白处按下鼠标左键。在出现的屏幕菜单上选择 ”open…”。点击“browser”。一个绿色的浏览器就出现了。再次利用这个屏幕菜单,选择 ”open…” ,并选择“workspace”。

如果你是一个新手,刚刚看完前面的教程,你应该已经有一个叫“My Stuff”的分类。把浏览器最左边的列表拉到最底端,应该就放在那里。

如果没有,我们需要新建一个地方来添加你的东西。在浏览器的左上面板中,点击右键呼出菜单[Option-click] (在苹果机上 – 教程以下都将Mac苹果的指令以这种方式显示)选择“add item…”并输入“My Stuff”并按回车。把列表拖到最下面,你会发现它已经被选中了。

我们再次白手起家,建立一个程序。我们先从一个空的类开始,这里我们要让他成为Morph的一个子类。在Morphic对象系统中,Morph是一个通用的图形类。我们现在看一下建立新类的一个模版。修改成下面这样:

Morph subclass: #TestMorph
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'My Stuff'

首先选择“Object”并输入单词“Morph”(M一定要大写)。选择“NameOfClass”并输入“TestMorph”。新建立的类就会成为Morph的一个子类。删除实例变量名和类变量名的文字内容。按Alt+s接受。[Command-s]

现在我们来到工作区并新建一个我们刚刚建立的类的实例。输入:

TestMorph new openInWorld.

选择这行并按下Alt-d [Command-d]或者从右键菜单中选择“do It” [Option-click]

一个新的图形对象——一个蓝色的方块,现在出现在左上角。这样你就搞定了一个图形对象,虽然几乎没写什么代码。你可以拾起这个对象并移动它。现在先用鼠标把它抓起来。注意了,当你拾起这个Morphic对象的时候,他下面投射出了一个阴影。这个可以帮助你确定你已经确切的把他抓起来了,而且在其他任何物体之上。如果有对象覆盖在上面,抓起这个对象会自动地把他拉到最前端,这时投影就会显示它现在在其他任何物体之上了。

你可能注意到奇怪的大写方式,但是在Smalltalk中,一个选择器只能是中间没有任何空格的一个字符串。所以我们约定在每个英文单词的第一个字母大写,然后去掉中间的空格连起来。其他语言也常在单词之间使用下划线或者破折号。

halo labels.gif
现在按住Alt并单击 [Command-click]这个蓝色的东西,这时你会看见一组不同颜色的小圈环绕在周围,我们称之为“光环”。这些光环每一个都是向这个Morph对象发送指令的一种快捷的途径。如果我的鼠标停在任意一个圆点上,一个小小的帮助气球会弹出来告诉我这个圆点能做什么。这个黑色的点上的气球说:“Pick up”。也就是,如果我拖动这个黑色的把手,他可以像我直接拖动这个蓝色方框一样。这样可能看起来是一个多余的功能,但是因为可能有时候Morph对象会有交互的行为,比如按钮,这个把手让你可以直接操作一些直接和鼠标动作相关的对象。

这里一个基本的想法是,大多数系统,如HyperCard,依靠模式来改变对象的大小等等。也就是编辑一个对象针对使用它是分离的。在Mophics系统中,你可以在任何东西上面使用光环不用考虑他是如何活动的。光环是在它运行时的一种安全的处理方法。你不需要中止他的按钮功能或者是其他的东西。如果这个东西是一个按钮,你可以无需激活就可以编辑它,也不需要关闭它。

让我们来看看光环到底能做什么。比如这个绿色的,它会产生一个副本。粉红的光环中的X会将它删除(移动到垃圾桶中)。另一个常用的功能是改变它的尺寸,由这个黄色的实现。现在暂时不要对它做其他事情了,尤其不要使用蓝色的按钮(在对象的左下角)。

现在让我们开始制作我们自己的对象吧。我们先写一些方法来自定义它。

我要做的第一件事情是让我们的对象在你用鼠标点击他的时候做点什么。这里我们要实现两个方法。点击浏览器,选中第三个面板中的 “no message”。把下面的代码粘贴到底部的面板并接受,Alt-s[Command-s]

handlesMouseDown: evt
^ true

如果这是我第一次在这个映像中接受一个方法,我必须先输入初始值,这个值将会跟着我修改方法一直记录。这个方法,handlesMouseDown,告诉Morphic对象有一个对鼠标敏感的东西。

下一个方法我们要添加的是当鼠标按下的时候实际要做的事情。继续,把下面的代码粘贴进去成为另一个方法。

mouseDown: evt
self position: self position + (10 @ 0).

将在你点击它的时候,这个代码会让对象向右边移动10个像素。试着点击这个对象看看会发生什么。

在这时我们已经有了两个方法。一个返回“true”说我们需要响应mouseDown:事件。第二个是对鼠标按下事件做出的响应,移动了对象的位置。

现在我想给这个Morph对象一些行为来重新绘制它自己。把这个方法粘贴到浏览器中并用Alt-s接受 [Command-s].

drawOn: aCanvas
| colors |
colors := Color wheel: 10.
colors withIndexDo: [:c :i |
aCanvas fillOval: (self bounds insetBy: self width // 25 * i + 1)
color: c].

现在,当你点击对象的时候,你会看到对象的完全不同的外观。让我们回头看看这个方法究竟做了什么。

这个方法的第三行(代码的第一行)是给临时变量‘colors’赋值。如果你看到两个竖线和一列变量名,那这些变量就是这个方法的局部变量,通常可以叫做临时变量。在Smalltalk和Squeak中,你可以不用自己声明变量——如果确实有系统会自动询问你。而且我们也没有任何“类型”所以你不用声明任何类型。任何变量都可以存放任何对象。所以我们把表达式(Color wheel: 10)的值赋给‘colors’。要显示这个方法作了什么,选择‘wheel:’并按下Alt-m查看实现器。你会看到有两个实现——选择Color class的那个。

我们在Squeak中经常做的一件事情是在方法之前放上一小段注释。第一行说了‘wheel:’是干什么的。他返回一个thisMany colors的一个集合,thisMany是一个参数。这些颜色会均匀分散在色环上。这是颜色的光谱。

如果很方便可以给出这个方法的一个例子,这也是我们经常做的。这里第二个注释是一个表达式你可以实际运行的。选中引号中的内容并按下Alt-d。在屏幕的最上方出现了一条展开的色环。这时一个在光谱上间隔的一些颜色的集合。(但不是最大饱和也是最大亮度,所以他们不会太刺眼。

在这个世界里面我们可以直接在屏幕上涂鸦,但他之后不会自动清除。所以我使用了屏幕菜单的“restore diplay”菜单项来去掉那些画在屏幕顶部的东西。

了解“wheel:”的含义在这里有点偏离主题了。我们知道现在这是一些颜色的集合。点击标题栏左边的 “X”关闭窗口。

这个方法的下一部分遍历这个颜色集合。

"下面是drawOn方法的一部分:"
colors withIndexDo: [:c :i |
aCanvas fillOval: (selfbounds insetBy: self width // 25 * i + 1)
color: c].

withIndexDo: 消息同时提供了一个颜色元素“c”和一个索引“i”,让我们能跟踪我们在列表中的位置。第一次循环索引会变成1,第二次是2……如此继续,直到10。我们只需要知道我们在列表中的哪个位置并增加缩进,让椭圆每次更小。我们发出消息”fillOval:color:“并在带上加上了括号的参数——一个矩形,比外部的矩形尺寸上缩小了 (((self width)// 25) * i + 1)像素,由(self bounds)返回——第二个参数是一个颜色。作为索引,i增加,缩进也增加,每次返回一个更小的嵌入矩形,每个矩形里面我们都会画一个椭圆并用颜色c填充。

当我们再看这个对象时,我们看见了10条颜色。这个看起来很不错啊,同时由于使用了发送给自身的消息“width”,作为他的一个参数,它就可以知道它实际上有多大。用Alt-点击[Command-click] 呼出光环并拖动黄颜色的点。你就可以改变它的大小了,不管变成什么尺寸,他都会一直重新绘制自身。

我下一个想做的事情是点击他并调用一些动画。既然这样那么第一件要做的事情是建立一系列的点。当我点击这个对象的时候,我希望他会沿着每个点来回移动直到所有的点都走过了。为了这个想法,我需要做些工作

Morph subclass: #TestMorph
instanceVariableNames: 'path'
classVariableNames: ''
poolDictionaries: ''
category: 'My Stuff'

回到浏览器,并点击“instance”按钮。然后会出现TestMorph的类定义。然后点击实例变量名的位置,在单引号中间,并输入“path”。这个会成为一个新的实例变量来保存一个显示我们对象位置的点的列表。现在我要点击名称为 “as yet unclassified”的消息分类。

当我添加了一个实例变量,我要知道它应该初始化为什么内容。当我们给TestMorph定义这个消息:

initialize
super initialize.
path := OrderedCollection new.

这一行代码“super initialize”为什么出现在这里?虽然类TestMorph的定义看起来十分简单,但path并不是他唯一的实例变量。在浏览器的第二个面板中,右键点击出现一个菜单,选择“inst var refs”,你会看见一个列表,里边列出了这个对象从超类继承的其他变量。我这里不想选择其中任何一个。如果我做了,他会把所有用到这个实例变量的方法列出来,不过,你可以看到这里有自类Morph继承的6个实例变量。

“super initialize”给我的超类,Morph类发送了初始化消息。当你给接收器“super”发送消息“initialize”的时候,他其实是发送给“self”,但是要保证是被继承的initialize的版本被调用。

我们的初始化的中心是:“ path := OrderedCollection new”。它将一个空的集合赋值给path。现在当我的TestMorph接收了发送的intialize消息,他首先完成Morph要做的初始化,然后,他再完成它自己的初始化。结果是所有从Morph继承的实例变量包括我新添加的都被初始化了。

我们实际上在这里又有一个约定,一般不会自动给一个新的对象发送“initialize”消息。但是,Morph对象总是给每一个新的Morph实例发送初始化消息。

这个TestMorph对象,现在就在屏幕上,并没有对我们新的代码获得初始化。我们在定义了新的 initialize 之前就创建了他。让我们先把它删除并做一个新的。用Alt-点击 [Command-click]呼出对象的光环。点击里面带个 X 的粉红色圆圈来删除这个对象。他现在被扔进了垃圾筒了。现在我们执行下面的代码来搞一个新的TestMorph(也许这次就可以初始化好了):

TestMorph new openInWorld.

我们现在要当你在我们的对象上点击鼠标的时候,让他做点什么事儿。我们现在已经有了一个“path”集合。把下面的方法复制进去并且接受:

startAnimation
path := OrderedCollection new.
0 to: 9 do: [:i | path add: self position + (0@(10* i))].
path := path, path reversed.
self startStepping.

我们在第一行创建了一个新的 OrderedCollection。在下一行,我们作了10次计算并把表达式的结果加到变量path上。我们保存了相对于目前位置的一些点,”self position”,加上一个我建立的点。建立的点的X坐标是0,“@”(可以建立一个点)后面的,Y左边是i的10倍,“i”是循环的变量,从0到9每次增加1。所以,最终的效果是建立的10个点,Y坐标是从当前坐标加上(0的10倍)到(9的10倍)并把这些新的点添加到path集合中。

接下来一行,我们把path扩展为(path, path reverse)。“reverse”会将任何集合用相反的顺序放置。逗号(“,”)操作符是另一个消息选择器用来连接两个集合。

我们所做的是把一个10个点的列表,从0到90,并且附加上一个列表,从90返回到0。这样我们现在就有20个点,出发并最终回到原来的位置。

该方法的最后一行把我们的Morph对象登记在一个可以持续发送“step”消息的一个引擎中。这是动画引擎的秒针,是动画的心跳。现在我们必须让我们的Morph对象理解step方法:

step
path size > 0 ifTrue: [self position: path removeFirst].

这个方法很容易理解。它说只要在点的列表中还有东西,也就是只要path集合的长度大于零,就移动自己。给自己发送消息“position”,并将从点集合中删除的第一个点作为参数传送进去。他在做每一步的之前检查并确保表中确实有东西可以删除。

从图象上看,它会立刻跳到下一个点。

现在这些东西都已经设置好要准备运行了,但是,他并没有开始动画。好吧,那是因为我们并没有真正通过向他发送开始动画的消息,所以我们才没有把这个球踢出去。我非得要做的一件事就是要试着找出发送消息给这个对象的途径,其实有一个最方便的方法就是让他自己给自己发消息,当鼠标按下的时候。

mouseDown: evt
self startAnimation.

这里有两种途径可以定义这个方法。你可以在任何现有的方法上粘贴(在浏览器下面的面板中),或者你可以点击“mouseDown”在右边的面板中,并修改我们现有的mouseDown。其实当你接受的时候都是一样的。

当你完成了上面的,再去点击你的Morph对象,你应该能看到发生了一些东西,它动了,太好了~,但是,他动得十分十分十分得慢。哎~,我失望了因为我以为Sqeak是很快的。其实呢,是默认仅仅每秒钟才给对象发送一次“step”消息.但是对象可以决定它自己需要多久被通知要执行“step”,我们只要实现一个消息“stepTime”:

stepTime
^ 50

它返回对象要求的 step 消息的时间间隔(以毫秒计)。现在,明显你可以要求两次 step 消息之间是 0 秒,但是你不会成功。这里存在一个基本的判断。

现在如果我们再次点击我们的对象,我们就能看到一个快得多的动画。我现在把它改成 10 毫秒,看看能不能达到每秒 100 帧。让我们看看能不能成功。好的,我不知道我是不是真的达到了 100 帧每秒,不过他看上去确实很快。

你知道,我是给“动画公司”工作的。这个动画起始并立刻停止,同时每个人都知道还有“渐进”和“慢出”来让他看上去更优雅。这里有一个小小的改进,使用了“i”的平方来让动画开始的时候比较慢。

startAnimation
path := OrderedCollection new.
0 to: 29 do: [:i | path add: self position + (0@(isquared / 5.0))].
path := path, path reversed.
self startStepping.

我只更改了这个方法里面的一行。我们现在准备在path中建立30个点,然后附加上反转的,一共60个点,另一个需要更改的是替代10次。最后,表达式将会是“841 / 5”。

现在动画就是开始下落时比较慢并且加速然后在他回来时又慢下来。事实上,他看上去像某种球在弹跳。

现在是一个很好机会让你自己和预先制作的Morphic对象玩玩乐。我们先拿一个装满对象的容器。如果你的系统中启用了标签,点击屏幕底部的 “Supplies”标签。如果底部没有标签页,使用屏幕菜单并选择“authoring tools…”。选择“standard parts bin”。无论什么情况你都能获得一大帮对象你可以拖到屏幕上,在你拖出来一个之后,Alt-点击[Command-click] 呼出光环,并按住红色圆圈呼出对象的内容菜单(contents menu)。这里是一些Morph对象:

  • A RectangleMorph (内容菜单中, 试试 “raised bevel” 和 “insetbevel”)

  • An EllipseMorph

  • A StarMorph

  • A CurveMorph (内容菜单中, 试试 “show handles”)

  • A PolygonMorph (f内容菜单中 , 试试”show handles”)

  • A TextMorph (可以用黄色的把手把文字块加宽)

  • [ignore the gradient filled Sketch Morph for now]

  • [ignore the green PasteUpMorph]

  • 画家的调色板可以在你丢下的位置开始新绘制一幅图。画完后,点击“Keep”按钮把图案变成Morph对象

  • [ignore the StringMorph]

什么是REBOL?

翻译:ShiningRay @ NirvanaStudio

简而言之,REBOL是提供了分布式计算和通信的一种轻量级软件技术。

REBOL技术包含两个部分:

REBOL语言

REBOL语言结合了编程语言和元数据语言(如XML)的概念。

这种结果是产生了一种新的语言,它提供了对现代网络分布应用的更有效解决方案。相比传统的语言,REBOL在更少的代码上提供了更强大的表达能力。REBOL应用程序和解决方案的一般典型的代码大小都在10KB的数量级上,而不是10MB。

REBOL发音为“reb-ol”(类似“rebel with a cause”中的),代表“基于相关表达式的对象语言”(relative expression based object language)。他第一次发布是在1997年到目前为止已经下载超过四百万次。他对客户端和服务器端应用程序都是十分合适的,就像下面所描述的。


你可以在《REBOL到底哪里不同》一文了解更多内容或者想迅速浏览一下REBOL语言的介绍,请看《REBOL 语言简介》。

X-Internet平台

X Internet是指可执行互联网(executable Internet),它是Internet发展的下一个阶段。

X Internet概念是本地计算机(客户端)成为像网络服务器一样强大,这样更多的计算责任可以被转换到客户端完成。这种方式由三个好处:

  1. 他改善了用户体验的质量。
    他让Internet应用程序运行的像本地应用程序一样快。Web设计造成的延迟不复存在。

  2. 他提高了服务器的性能。

    由于客户端作了更多的工作。这就让你在同一台服务器上可以进行的连接次数大大增加。现在典型的是大约10到50倍于原来的连接数量。这就好像以一台的代价可以做10-50台或更多服务器的事。

  3. 他使得网络通讯更加有效。

    应用程序不需要每次连接都传送用户界面信息(如:网页)。取代的是指需要发送用户产生的动作相关的数据和代码。这让X Internet程序仅仅占用目前典型Web应用程序所用带宽的1/10到1/50。

是不是说X Internet反对“瘦客户端”计算模型?当然不是,如果你使用REBOL。因为REBOL的先进的语言设计,X Internet应用程序将和大多数基于web的解决方案比,显得特别得小。这就是我们所谓的轻量级。当应用程序构建得较为完善,他们将花费更少的时间和金钱来进行开发,并且很容易维护和升级。

如果要了解X Internet和REBOL解决方案,请参见我们的IOS概览页面

为什么艺术家应该使用Ogg Vorbis格式

作者:by daniel james
翻译:ShiningRay @ Nirvana Studio

计算机,控制和内容产业

主流唱片公司都想去控制人们听音乐的方式,他们软硬兼施,同时使用人们将来要购买的软件和硬件。“内容”,对我们大多数来说也就是音乐和电影,是一个全球产业,每年都可以创造几十亿美元的价值。

通过网络分发数字媒体可以以最小的代价传送数据,这点并没有被内容产业所忽略。然而,内容产业把互联网和其他网络看作为一条管道,从他们的制作室到你的眼睛和耳朵之间——当然还有一个返回的管道,是从“消费者”的钱包到他们的银行账户中的。

数字音乐的出现,内容产业认为通过他来消除传统的物理媒介的音乐发布的成本,可以极大增加利润。但是还有一个问题——数字音乐,已经被离散为一系列数字,所以可以被复制无数次而不会损失任何质量。如果大多数收听音乐的人还不具备复制这些东西的条件的话,这还不是一个问题——这是针对计算机的。

所以内容产业可以做两件事情——用某种方式改变数字音乐文件这样就让你不能复制,或者改变你的电脑让你不能做你要做的事情。前一个想法已经失败了,因为你对数字做任何动作都可以被还原,只要你知道其中的秘法——CSS和SDMI已经被证明是很容易被破解的。

改变你的电脑是内容产业是保证内容管道密封的唯一可行的方法。他们可以通过软件来做到这一点——说服你使用可以监管你对音乐使用的播放器和格式——或者通过卖给你像CPRM卡之类硬件,也可以做到。

当然,请不要把你为朋友进行剪辑,或者复制一份在你的便携设备上播放的情况和在世界上很多国家都存在的盗版CD和磁带业务相混淆。很显然,那些贩卖者艺术家们的作品而不给他们一份合理的报酬,是不对的。流行音乐的历史常常出现艺术家们没有获得报酬,而且并不是因为家庭内部的复制或者是商业盗版。

自由共享 VS 收费播放

数字技术和网络分发消除了艺术家和音乐听众之间的中间障碍,但是内容产业还想把这些障碍放回去。他们可以利用他们所能控制的一个受知识产权保护的音乐格式和软件、以及人为的限制来达到这个目的。

内容产业告诉艺术家们他们需要这种对听众的控制,以便他们能继续为录音的过程和市场销售来付钱——还有艺术家的薪水。

为什么艺术家有问题呢?毕竟,你希望你的音乐被人听到,但你也希望能从你的作品中获利。所以你只能参与到内容产业的圈子中并遵守他们的协议——但是一个更大的问题是他们对数字音乐的看法是不能被改变的。

a) 被听见

如果每次只卖给我们一首歌,大的唱片公司那会高兴死了,因为市场成本和风险可以最小化。今天我们听流行音乐电台,极其短的播放列表也许会让你相信我们已经在朝这个方向走了。内容产业想要的是大批量的销售,这和音乐的多样性是违背的。如果你是今年的流行组合,那太好了——否则,甭想。

结合了点对点文件共享(就像原来的Napster)的自由下载,确实是一种极其廉价的方式来让你的音乐被全世界听见。如果你是一个没有靠山的艺术家,那也许这是唯一的途径。他很有效地利用带宽,因为音乐可以通过其他人的计算机进行传送——也可以帮你从ISP哪里省下很多钱。

但是内容产业不允许自由的点对点共享,因为这里面没有他们可以榨取的差价。所谓它对CD销售产生极大影响的说法,显然是错误的——根据同样的逻辑,他们也必须禁止广播和电视上的音乐,只要人们可能听见音乐的地方。

唱片公司曾试图保护CD销售,采取了只发布剪辑,或者是使用很低的音质的方式。这就让人们不会去分享他们,但是它实际上有没有鼓励人们去买一个专辑呢?
这显然是毫无道理的,特别是当你考虑到你还可以在广播或者电视上听到整个音乐的全部内容以及是最好的品质——假设内容产业认为你的市场潜力足够大然后会给你播放。

b) 获利

让我们看一下现存的CD盈利模模式,同时假设每买出$10的CD,就要付给你10%,虽然现实中扣除了成本之后,你也许不会得到这么多。我们假设一张CD有10首音乐,这样你每首音乐可以得到10分钱。

在内容产业的数字盈利模式中,假设你同样可以得到每首歌下载费用是10分钱。为了能得到这个数字,唱片公司和CD一样对每十首歌曲收费,也就是一首音乐1美元。

但是听众购买下载音乐的动机在哪里呢?他们使用了带宽来下载,他们又不能免费得向其他计算机传送东西(这些东西不能共享)同时他们甚至不能备份。如果你的计算机出现了一个错误,同时你正在获取一个新的内容,你只好对你的唱片集说拜拜了。

如果数字音乐是免费的,人们不会介意下载他们——Napster证明了。但是CD的音乐是在设备中更有弹性的、便携性的而且可以很方便得在朋友中分享。付费下载必须得十分便宜才能够可以CD竞争——那么哪里还有多少剩余价值可以付给艺术家呢?

更好的是,提供免费的官方下载并且鼓励人们去分享他们。听众们会珍惜这种听音乐的机会,在他们购买你的CD之前,你也会省去花在促销上的大量的时间——和大量的金钱。

MP3怎样?

那么为什么不可以就用MP3呢?他已经很流行了,而且听众们可以很方便得共享他。问题是那些进行MP3压缩的人们,其实已经落入了内容产业的布局,变成了另一个中间人。免费的MP3编码器一个个都倒了,因为这个技术的所有者要求使用它的软件要付版权费。

虽然起初吹捧说是一个免费的格式,并且和无成本的音乐下载结合,但是MP3背后的技术一直都是有专利权的。在过去的“免费午餐”的市场中,一个产品会一直以免费形式发布直到它变得流行了,这时候所有者就会把它转变成一个商业产品。

作为一个艺术家,MP3的版权费用意味着当你自己的每一首音乐每次被下载的时候,你都必须要付一定的费用。也许现在只是几分钱,但是控制这个技术的人会在未来随时找你麻烦。这就意味着你不能再分发免费的音乐即时你想这样——除非你自己掏钱付MP3的版权费用。

Ogg Vorbis 格式就是为了替代MP3 的,他没有技术上的任何税——他完全是免费的,而且以后也将是,因为他使用的是自由软件的许可证。Ogg文件已经得到了一些播放软件的支持,如Winamp,同时编码器也可以免费获得。他的可变比特率的技术,同样大小的Ogg格式的音乐听起来就是要比MP3的好。

Ogg Vorbis格式是在内容产业的控制之外的。他们也可以使用它,但是他们不能阻止你去使用它。同时如果你要想让大家不受限制地听你所有的音乐,或者和朋友分享,你就可以这么做。

但是Ogg格式不会流行起来除非艺术家们可以让这种文件给大家获得。所以如果你想支持自由的音乐,请从 www.vorbis.com下载免费的编码器并把一些 .ogg 格式的文件放在你的网站上。


请把对本文的评论发送给 daniel at mondodesigno dot com

相关的文章: