Connect介绍(Just Connect it Already)
发布于 13 年前 作者 lenzhang 7092 次浏览 最后一次编辑是 8 年前

<p align=“left”>Connect介绍(Just Connect it Already)</p> <br/><p style=“text-align: right” align=“left”><a href=“”>原文链接</a></p> <br/><p align=“right”>                             <em><span style=“text-decoration: underline”>Translated by lenzhang(<a href=“”></a>)</span></em></p> <br/><p align=“right”><em><span style=“text-decoration: underline”>Reviewed by goddyzhao(<a href=“”></a>)</span></em></p> <br/><p align=“right”><em><span style=“text-decoration: underline”>感谢社区志愿者们在翻译过程中所提供的帮助</span></em><em></em></p> <br/><p align=“left”>Now that the core APIs of node are really starting to stabilize, I’m moving my attention to helping stabilize the framework scene. One of the things I found really neat from Ruby was the <a href=“”>Rack</a> server interface. It allowed any server that followed the spec to host any app that followed the spec. Also (and this is the important part for node) is allowed for generic middleware libraries to do common tasks and functions in a very aspect oriented manner.</p> <br/><p align=“left”>现在node的核心api已逐渐稳定,我开始着手提高框架的稳定性。我发现Ruby的<a href=“”>Rack server interface</a>很整洁。允许任何遵循规范的服务器主机可以方便的部署任何符合规范的应用程序。同时(这也是node很重的一部分)也允许中间件库以面向切面的方式来完成通用的任务和功能。</p> <br/><p align=“left”>My employer, <a href=“”>Sencha</a>, has sponsored <a href=“”>TJ Holowaychuk</a> and I to write a middleware system for node called <a href=“”>Connect</a> in an effort to foster common development in the node community.</p> <br/><p align=“left”><strong>UPDATE</strong> This article has been updated to use the new connect middleware API.</p> <br/><p align=“left”>我的雇主, <a href=“”>Sencha</a>, 资助 <a href=“”>TJ Holowaychuk</a>和我给node写一个中间件系统,名为: <a href=“”>Connect</a> ,目的是以此来推动node社区更快的发展。</p> <br/><p align=“left”><strong>UPDATE</strong> 这篇文章已经更新为使用新版的connect <br/>middleware api。</p> <br/><p align=“left”><strong>So What’s New?</strong></p> <br/><p align=“left”><strong>有何不同? </strong></p> <br/><p align=“left”>Actually there isn’t a lot new here. But then again, there was nothing new about node either. Node uses non-blocking IO for fast scalable servers. That’s been known about for years among the C community. It uses event based, single thread javascript for logic. That’s exactly what the browser has. Add these together and we all see the huge splash it’s made. It’s the unique combination of some simple but complimentary ideas that really make these projects zing.</p> <br/><p align=“left”>实际上并没有什么新的东西。但话又说回来,node本身也没有什么新的东西。Node使用非阻塞io实现快速可伸缩服务。这种方式在c社区早已被人熟知好几年了。它同时使用基于事件的单线程javascript来实现逻辑。</p> <br/><p align=“left”>这正是浏览器的特性。而它将这些组合起来就立刻受到了欢迎和追捧。也正是对这些简单而又值得称赞的创意的独特组合使这个项目火起来了。</p> <br/><p align=“left”><a href=“”>Connect</a> tries to abstract and repackage node as little as possible. As a result, the API is fairly node specific, but there aren’t a lot of leaky abstractions dripping all over the place. It’s fairly solid considering the short time it’s been in development so far. <a href=“”>Connect</a> adds one new unique aspect to node’s HTTP server and that’s the idea of layers.</p> <br/><p align=“left”>Connect 尝试尽可能小的抽象并重新封装node。最终,其API和node一致,但是提高了抽象层的质量。到目前为止,就从这段短暂的开发期来看,Connect还是相当高质量的。Connect 为node的HTTP服务端添加了一个独特的切面。这也是Connect的精华所在。</p> <br/><p align=“left”> <strong>The Integration Problem</strong><strong></strong></p> <br/><p align=“left”><strong>整合的问题</strong></p> <br/><p align=“left”> In a normal node HTTP server you usually see code like this.</p> <br/><p align=“left”>在node HTTP服务器中,代码通常如下所示:</p> <br/><p align=“left”><a href=“” target="_blank">node-http.js</a></p> <br/> <br/><pre escaped=“true” lang=“javascript”>var http = require(‘http’); <br/>// 开启一个HTTP服务器 <br/>http.createServer(function(request, response) { <br/>// 对每个请求都响应“Hello Connect”文本 <br/>response.writeHead(200, {“Content-Type”: “text/plain”}); <br/>response.end(“Hello Connect”); <br/>}).listen(8081);</pre> <br/><p align=“left”>And all requests will be served:</p> <br/><p align=“left”>所有的请求都将返回如下结果:</p> <br/><p align=“left”>HTTP/1.1 200 OK <br/>Content-Type: text/plain <br/>Connection: close <br/>Transfer-Encoding: Identity</p> <br/>Hello Connect <br/><p align=“left”>This works great for when you want fast synthetic benchmarks or always want to return the same response for every HTTP request. In most apps, however, this isn’t the case. You want some form of request routing. Also you’ll want nice enhancements like response body gzipping, smart caching, request logging, pretty error handlers, etc…</p> <br/><p align=“left”>上述例子,满足快速测试或者总要求服务器对每个HTTP请求返回同样的结果,这样的需求。但是在大多数程序中并不是这样的情况。你需要某种形式的路由。同时你还需要很好的增强的功能,比如返回内容的gzip压缩,自动缓存,请求日志,漂亮的错误处理等等。</p> <br/><p align=“left”>Implementing all these things over and over for each project is a royal pain since they are somewhat non-trivial and usually a project in and of themselves. So ideally the node community has a collection of modules that we can use in common to solve these common tasks. The only roblem is that there is no accepted spec to follow. All these libraries have their own style and way to integrate. This is great for innovation, terrible for someone trying to just get work done and quickly.</p> <br/><p align=“left”>一遍又一遍的在每个项目中都实现它们实在是太痛苦了,而这些功能在某种意义上又是不可或缺的并且都是每个项目中的一部分。所以理想的是node社区收集这些常用的模块来解决常见的问题。唯一的问题是需要一个规范来约束。我们常见的其他语言的库都有他们自己的风格和方式来整理。这将促进创作,遏制随意的行为。</p> <br/><p align=“left”><strong>Layers to the Rescue</strong></p> <br/><p align=“left”><strong>层的出现解决了问题</strong>(原文为:层的拯救)</p> <br/><p align=“left”>So taking the ideas from Rack and ejsgi, we introduce the idea of layers to the code handling the HTTP request and response. An app is structured like an onion. Every request enters the onion at the outside and traverses layer by layer till it hits something that handles it and generates a response. In Connect terms, these are called filters and providers. Once a layer provides a response, the path happens in reverse.</p> <br/><img class=“alignright” style=“margin: 0px 0px 10px 10px” src=“” alt="" width=“214” height=“227” /> <br/><p align=“left”>通过借鉴Rack和ejsgi,我们提出分层来处理http请求和响应的想法。一个程序就像一个洋葱,每一个请求逐层进入洋葱直到遇到一层处理它,并且返回一个响应。在Connect的术语中,我们叫这个filters和providers。一旦某一层提供了response,将直接返回结果。</p> <br/><p align=“left”>The <a href=“”>Connect</a> framework simply takes the initial request and response objects that come from node’s http callback and pass them layer by layer to the configured middleware modules in an application.</p> <br/><p align=“left”>在程序中Connect框架简单的将node的http回调函数中的request和response一层又一层的传递给所配置的中间模块。</p> <br/><p align=“left”>The example from above, converted to a <a href=“”>Connect</a> app looks as follows:</p> <br/><p align=“left”>上面的例子转换为connect程序看起来像下面这样。</p> <br/><p align=“left”>connect-http.js</p> <br/> <br/><pre escaped=“true” lang=“javascript”>var Connect = require(‘connect’); <br/>Connect.createServer(function (req, res, next) { <br/>// 每个请求都响应"Hello Connect"文本. <br/>res.simpleBody(200, “Hello Connect”); <br/>}).listen(8080);</pre> <br/><p align=“left”>And request will output this:</p> <br/><p align=“left”>所有的请求都将返回如下结果。</p> <br/><p align=“left”>HTTP/1.1 200 OK <br/>Content-Type: text/plain <br/>Content-Length: 13 <br/>Connection: close</p> <br/>Hello Connect <br/><p align=“left”><strong>Walkthrough Writing Layers and an Application</strong></p> <br/><p align=“left”><strong>尝试编写层和程序</strong></p> <br/><p align=“left”>Let’s go through a simple app from the top down. It will serve JavaScript files from a folder, cache the result in ram, and log the request and responses. We’ll implement our middleware layers from scratch to understand how they work. There are better versions of these built-in.</p> <br/><p align=“left”>让我们尝试从头到尾完整地编写一个简单的程序。程序处理文件夹中的JavaScript文件,将结果缓存在内存中,并以日志形式记录请求和返回情况。我们将实现我们的中间件来理解它是如何工作的。。不过,这只是作为例子而已,并不是最好的实现方式。</p> <br/><p align=“left”>app.js</p> <br/> <br/><pre escaped=“true” lang=“javascript”>var Connect = require(‘connect’); <br/>module.exports = Connect.createServer( <br/>require(’./log-it’)(), <br/>require(’./serve-js’)() <br/>);</pre> <br/>  <br/><p align=“left”>An app is just a call <br/>to Connect.createServer with several handlers in a row.</p> <br/><p align=“left”>上述程序只是简单调用了Connect.createServer,同时传递了一些处理程序。</p> <br/><p align=“left”>All Connect layers are simply node modules that export a setup function that returns a handle function. The setup function is called at server startup and you can pass in configuration <br/>parameters to it. Then on each request, you have the option at this point to either: A) Serve a response using the res parameter. or B) Pass on control to the next layer in the chain using the next parameter.Since you have raw access to the node request and response objects and the full JavaScript language, the possibilities are endless.</p> <br/><p align=“left”>所有的Connect层只是简单的node模块,声明了一个起始函数,这个函数返回一个处理程序。这个起始函数在server启动的时候被调用。你可以传递配置参数到函数里。然后在每次请求的时候,你都可以做如下操作:A)使用res返回一个请求结果。B):使用next参数跳到下一层。因为可以直接访问node request和response对象和整个javascript语言,所以将有无限的可能。</p> <br/><p align=“left”><strong>Serve Some Files</strong></p> <br/><p align=“left”><strong>提供一些文件服务</strong></p> <br/><p align=“left”>Most apps will want to serve some static resources, so let’s write a middleware that serves javascript files based on the request url.</p> <br/><p align=“left”>大多数的程序都想要提供一些静态文件服务,所以我们一起写一个基于request.url的中间件来提供javascript文件服务。</p> <br/><p align=“left”>serve-js.js</p> <br/> <br/><pre escaped=“true” lang=“javascript”>var fs = require(‘fs’); <br/> <br/>module.exports = function serveJsSetup() { <br/> return function serveJsHandle(req, res, next) { <br/> // 处理所有相对此进程的文件 <br/> // 要注意的是,这里为了代码简单,并没有考虑太多的安全问题 <br/> fs.readFile(req.url.substr(1), function (err, data) { <br/> if (err) { <br/> next(err); <br/> return; <br/> } <br/> res.simpleBody(200, data, “application/javascript”); <br/> }); <br/> }; <br/>};</pre> <br/><p align=“left”>Here we are using the built-in node library ‘fs’ to read the requested file from the hard-drive. Then we’re using the Connect provided helper simpleBody on the http response object. Nothing fancy or complicated here.</p> <br/><p align=“left”>这里我们使用内置的node库‘fs’来读取所请求的文件。接着我们使用Connect提供的http response对象的simpleBody方法。这一点也不晦涩难懂。</p> <br/><p align=“left”><strong>Log It</strong></p> <br/><p align=“left”><strong>记录日志</strong></p> <br/><p align=“left”>Whenever there is a problem with a server, it’s really great to have a log-file somewhere to trace what went wrong. This log module will output a line when a request comes in through the layer, and then another on the way back out.</p> <br/><p align=“left”>当服务器有问题的时候,能够有日志文件来跟踪问题发生的原因真是太好了。当请求过来的时候日志模块将会输出一行request日志,和一条返回日志。</p> <br/><p align=“left”>log-it.js</p> <br/> <br/><pre escaped=“true” lang=“javascript”>module.exports = function logItSetup() { <br/> <br/> // 初始化计时器 <br/> var counter = 0; <br/> <br/> return function logItHandle(req, res, next) { <br/> var writeHead = res.writeHead; // 存储初始函数 <br/> counter++; <br/> <br/> // 日执行时记录请求 <br/> console.log("Request " + counter + " " + req.method + " " + req.url); <br/> <br/> // 封装writeHead函数,将其注入到层的出口路径中 <br/> res.writeHead = function (code, headers) { <br/> res.writeHead = writeHead; // Put the original back <br/> <br/> // 以日志形式记录响应 <br/> console.log("Response " + counter + " " + code + " " <br/> + JSON.stringify(headers)); <br/> <br/> res.writeHead(code, headers); // 调用初始函数 <br/> }; <br/> <br/> // 传递给下一层 <br/> next(); <br/> }; <br/>};</pre> <br/><p align=“left”>The setup function is a great place to setup variables used by the middleware across requests. In this case we’re initializing the counter for the logger.</p> <br/><p align=“left”>在起始函数中初始化跨请求的变量是很方便的。因此为logger初始化了计数器。</p> <br/><p align=“left”>In the handler we are using a wrapping idiom to hook into the call to writeHead. In JavaScript functions are values just like anything else. So a great way to wrap functions is to store a reference to the original implementation in a closure variable. Replace the function with a new one, and in the first line of the new function, put the old function definition back. Then on the last line of the replacement function call the original. This is a simple and efficient way to hook into existing object methods since they just look for properties by name and not references to actual function objects.</p> <br/><p align=“left”>在处理程序中,我们使用了一种包装句式来挂接到writeHead。在Javascript中函数也是值就像其他的数据类型一样。所以比较好的办法是将函数的引用存储在变量中来构造一个闭包变量。然后使用新函数函数来替换这个,在新函数的第一行将原函数的定义放回来。在新函数的最后一行调用原函数。由于javascript通过属性名而不是引用来标识真正的函数,所以这是将函数挂接到已有对象非常有效的办法。</p> <br/><p align=“left”>The standalone console.log call will be called at the beginning of each request cycle, and the nested console.log will be called on the way out by means of the nested writeHead function.</p> <br/><p align=“left”>独立的console.log将会在每个请求周期中被调用。嵌套的console.log将会在writeHeader每次被调用的时候调用。</p> <br/><p align=“left”><strong>Built-in Middleware</strong></p> <br/><p align=“left”><strong>内建中间件</strong></p> <br/><p align=“left”>Connect comes with several built-in middleware layers for easy use. A much more robust version of this example could be written using the built-in modules.</p> <br/><p align=“left”>Connect内建了很多易用的中间件。我们之前的例子可以使用这些组件构建一个更强大的程序。</p> <br/><p align=“left”>app2.js</p> <br/> <br/><pre escaped=“true” lang=“javascript”>var Connect = require(‘connect’); <br/> <br/>module.exports = Connect.createServer( <br/>Connect.logger(), //以终端通用日志格式记录响应 <br/>Connect.responseTime(), //添加一个包含时间信息的特殊头信息 <br/>Connect.conditionalGet(), // 为了节省带宽,添加HTTP 304响应头 <br/>Connect.cache(), // 为了提高性能,添加一个临时内存缓存 <br/>Connect.gzip(), // 有需要的时候,对输出流进行Gzip压缩 <br/>Connect.staticProvider(__dirname) // 处理当前目录下所有的静态文件 <br/>);</pre> <br/><p align=“left”>This has proper error-handling, proper HTTP headers, and all sorts of other bells and whistles that are required from a production web server.</p> <br/><p align=“left”>上面的程序有很好的错误处理,很好的HTTP头,和其他各种各样的生产环境下的web服务器所需要的组件。</p> <br/><p align=“left”><strong>Future and Goals of Connect</strong></p> <br/><p align=“left”><strong>Connect愿景</strong></p> <br/><p align=“left”>Connect is currently in alpha state. We’re looking for community feedback and hope to stabilize into a beta in the next week or so.</p> <br/><p align=“left”>Connect当前仍然处在alpha状态。我们在寻找社区的反馈,希望在未来一周或者差不多的时间可以使之进入beta版。</p> <br/><p align=“left”>Also what’s really needed is for some real frameworks and apps to be written using Connect as a base. TJ is using it internally for a project at work and I plan to convert <a href=“”>wheat</a> (The engine to this blog) to use it.</p> <br/><p align=“left”>当然我们仍然需要一些真正基于Connect的框架和程序。TJ是一个基于Connect的项目,我打算将wheat(本blog的引擎)替换成TJ。</p> <br/><p align=“left”>The true goal of Connect is to help the node community work better together. Connect is the combined effort of some JavaScripters from the node community who want a base system to build world-class web frameworks from.</p> <br/><p align=“left”>Connect的真正目的是帮助node社区能够更好的协作。Connect是一些来自node社区JavaScript编程者共同努力地结果,他们想要一个基础的系统来构建出世界级的web框架</p> <br/><p align=“left”>There has been a lot of discussion on the topic of middleware and now is the time to write some code, use it, and do it. The popularity of JavaScript itself is proof that what really succeeds is real-world implementations, not substance-less discussions on the very best way to do something. Like node, our goal is to make something simple, but correct, and let others build from there.</p> <br/><p align=“left”>我们已经花了很多篇幅讨论中间件,现在是时间写一些代码里,使用它,并行动。javascript的流行证明真正的成功是去实现,而不是毫无实质的讨论。就像node,我们的目标是做一些简单但是正确事情,使其他人基于此构建。</p> <br/><p align=“left”><strong>What you Should Do</strong></p> <br/><p align=“left”><strong>你应该做什么</strong></p> <br/><p align=“left”>Connect is cool, I gave two presentations on it in the past week at <a href=“”>txjs</a> and <a href=“”>swdc</a> and people loved it. TJ and I have done all we can for now and need some community feedback in order to move on. If you are interested in node and want to help shape the future of web frameworks please do the following:</p> <br/><p align=“left”>Connect是很酷的,我上周在txjs和swdc做了两次演讲,大家都很喜欢他。TJ和我已经做了我们目前为止能做的所有事情,我们需要得到一些社区的反馈才可能够继续走下去。如果你对node感兴趣并且希望能够帮助塑造web框架的未来,请做以下的事情:</p> <br/> <br/><ul> <br/> <li>Install node <br/>if you haven’t already. (I suggest using <a href=“”>ivy</a>)   如果你还没有安装node,请安装</li> <br/> <li>Clone Connect.  获取一份Connect的代码</li> <br/> <li>Go through the examples in the code-base. (The app.js file is launched with  the connect executable) 运行code-base里的例子</li> <br/> <li>Write your own code using Connect. (Or port your favorite node framework)  使用Connect编写你自己的代码,或者请提交你最喜欢的node框架代码</li> <br/> <li>Send feedback  through <a href=“”>github</a> and the normal node community channels. (irc and mailing list)  通过github反馈或者其他社区途径(irc或者邮件列表)</li> <br/> <li>Tweet about it to spread the word. (This only works if everyone uses it)在tweet上发与之相关的推文来宣告全世界(只有全世界都用tweet才可以)</li> <br/></ul> <br/><p align=“left”><strong>Deploying Connect Apps</strong></p> <br/><p align=“left”><strong>开发Connect程序</strong></p> <br/><p align=“left”>See the deploying-node-with-spark article for tips on how to set up a production server using <a href=“”>Connect</a> and <a href=“”>Spark</a>.</p> <br/><p align=“left”>阅读deploying-node-with-spark文章来获取一些有关如何建立生产环境的信息。</p> <br/>

3 回复

哇, 很酷. 有了它nodejs提高了一层境界