The main advantages/differences over Connect/Express at this point are:
- Patterned after WSGI/Rack. This implies some significant differences in the API.
- Built on node 0.4; takes full advantage of streams.
- Built-in support for streaming multipart parsing/file uploading (uses node-formidable's parser).
- Built-in support for gzip encoding, URL rewriting, and URL mapping.
- RFC-compliant support for content negotiation using the Accept, Accept-Language, Accept-Encoding, and Accept-Charset HTTP headers.
- A specification with middleware to enforce it.
So nothing? Streams, multiparts, gzip, that is all in Connect/Express. I'm sorry but adhering to some spec that few people use or care about is hardly an advantage over Connect. Wouldn't it be trivial for Connect to adhere to the spec anyways?
tjholowaychuk has done extensive testing/benchmarking of Connect/Express. I'd really like to see some benchmarks and code coverage.
Connect/Express do little more than set up a callback chain for you that passes along the request and response objects it received from node's request listener.
Strata gives you a sane environment modeled after the WSGI/Rack/CGI model, which has been the backbone of web servers for years.
Also, Strata's API matches the WSGI/Rack model. This may seem small, but in practice it's a significant benefit.
Add to that the list of features cited in the release email, including native support for streams in node 0.4.
It's easier with Connect/Express haha, stream.pipe(res) vs callback(200, {}, stream). We've been down this road in the past, in fact Express started out a bit more like this, abstracting away node's req/res, but it's a leaky abstraction that doesn't really provide any real benefit. As far as "filtering" on streams etc, this is something node core should provide (and will be shortly) so it's irrelevant in that respect as well
I'm still pretty new to the idea of node.js, but a couple things stand out to me. This framework uses a session cookie as a way to store some data, not just as a user/session id/authentication. Thus, they seem to be strongly pushing you towards an external session store such as memcached or a database. (I suppose that is an inherit limitation of node.js, at least as commonly used)
From what I understand, a Node/V8 instance really only utilizes a single CPU, yes? As a request does I/O, some other piece of code is executed until (some time after) the async I/O completes. A CPU can be effectively timesharing over several pseudo-threads, but to use a SMP chip/server, you have to run multiple instances, with a front-end load balancer. I guess this would drive one towards an external session data solution, barring some kind of sticky session and a reason to be sticky.
Isn't an external session store practically mandatory regardless of your backend?
I'm no fan of Node, but even if you have $100k machine running the JVM with a thousand threads you will (if successful) eventually need a second machine, and a third, etc. Not to mention persistence (what if you need to restart the app server?).
Sessions belong in a database of some kind unless you can get away with signed cookies.
no it's not mandatory. We use Play and it's super stateless. nothing more than client side cookies. forced us into scalable, share nothing design. love it!
The Lift web framework does this too, as does Apple's Web Objects. David Pollack, creator of Lift, wrote a good explanation of the benefits on Quora, will post it shortly.
EDIT: Strata ships with cookie-based sessions but it's pretty easy to write your own storage middleware: var app = strata.redisSession(app, options);
After seeing how awesome the default cookie-based sessions are in Rails, I'm not sure I'd ever care to go back. Maybe for a high security banking app where breaking the cookie encryption could have catastrophic, permanent repercussions? Short of that, load balancing shared-nothing cookie-based sessions is a joy. Gotta agree with the Lift guys there.
"In practice, scaling a Lift site is much much easier than scaling a LAMP site. Why? Well, state exists someplace. If it exists in the JVM, you get a lot of performance benefits and stability as well as, in Lift's case, lots of security. Contrast that with sessions in memcached. "Whoop, memcached went down, there go a pile of sessions." "Whoops, we've got a new memcached hashing algorithm, there go all the session." "Whoops, Google just crawled us creating 200,000 new sessions pushing all the but the active sessions out of cache." "Whoops, the Ruby runtime just went wild, ate all the VM on one of our boxes, memcached went down..." So, you try storing sessions in some wacky shared version of MySQL. This solution requires tons of hardware and a team of make sure that the sharing code is correct, etc. Contrast that to using Nginx, Jetty and session affinity. It's about 4 hours of setup time and it just works. See http://blog.harryh.org/post/7550... So, talk to a Facebook engineer about the challenges they go through to manage state between the front end, memcached, MySQL, etc. Compare that to Twitter with the famous fail whale. Compare that to Apple's store and the iTunes store which are written on WebObject (which is highly stateful.) Lift apps running at scale typically require 7% of the front end resources of LAMP app. The Lift apps that are running at scale (Foursquare and Novell pulse are two) do not have the kind of scaling issues associated with LAMP sites that have similar traffic patterns. Scaling with Lift is neither tricky, nor risky. It's simple. It's known. It's proven. Scaling with LAMP is playing whack-a-mole with state and that only becomes a problem at scale."
All the session data is stored (and signed) in a cookie on the client side. Why would you need an external session store then? Isn't it more like the opposite; with this solution you never need an external session store, no matter how many instance you run?
I think he means that by using cookies, you're constrained to their limits. So if you have a large amount of data to pass around, you'll be forced into something external.
The fact that Strata uses cookies for sessions is just a personal preference, and certainly isn't something that is forced upon you. It would not be very difficult to add support for file or database-backed sessions into the core distribution.
To be honest, this doesn't look like much of a web framework. The examples show views being built up using string concatenation... reminds of my pre-templating days building snippets for jQuery.
Really though, everything shown can be easily replicated with a bit of Connect middleware, and it'd be cleaner. I don't see the point?
Node.js is the new hotness, you've gotta write a framework (as opposed to tools that analyze JS code and point out potential pitfalls and bugs, or tools to build and deploy complex JS solutions, or tools/framework that support better unit-testing for JS both server and cliend-side) to get street-creds these days.
I would go as far as saying that it does not offer much more than what you can do with the http module. But if you take away built-in template engines, orm, active reccord and an CRUD generators (both for web forms and APIs), the same applies to 90% of the frameworks out there.
I work with pretty messy code bases and every other week find myself wondering if using a framework did really helped more than it introduced unnecessary code.
That's like saying that WSGI/Rack don't do anything more for Python/Ruby than their respective HTTP libraries. When you understand the rationale behind those libraries you'll understand Strata.
No. Node.js itself is an event machine, node's http module is nothing but a particular plug-able even handler framework. That is not the same writting an application in python that uses the http library.
The latest requires considerably more work, the prior is much simpler.
Granted I blazed through the docs and am not a node expert, but for a streaming web framework I could not find a reference to streaming responses. It is very Rackish, all examples I found point to calling callback(200, {}, "Hello world!");
What I don't get, is you have to fight with Rack to get anything close to async, yet here comes a web framework for an async environment modeled after Rack, go figure.
I assume (meaning I'm about to make an ass out of u & an ass out of me) the answer is to pass a stream in place of the "Hello world!" string, but that would cause hell with any middleware expecting a completed response.
The Strata spec states that a body may be either a string or a stream. All Strata middleware that needs to modify the body in some way must account for this. It's not difficult to do a "if (typeof body == 'string')".
Strata borrows many concepts from Rack, but not the synchronous part. Instead of expecting your app to return something, Strata gives you a callback that you can use to serve the response when you're ready.
I admit the manual could probably give a better example of streaming a response. For now, check out the static file serving middleware (https://github.com/mjijackson/strata/tree/master/lib/static....). You'll see that the response body is simply a readable stream to the file on disk.
The examples use string concatenation for the sake of simplicity. Strata doesn't attempt to tie you to any particular templating library because everyone has their own preferences and there are so many of them out there.
Connect middleware has never been especially clean for me. Every app I end up working on, we slowly grow this hideous mess of middleware that starts off like:
And tends to end with around 40 lines of checking URLs and other various bits of information to know which middleware can be chained in, and in which order that doesn't cause them to conflict with one another.
If Strata can solve this mess, then it's definitely a step up in my opinion.
It's unfortunate this doesn't just use JSGI, which is much closer to Rack/WSGI than Connect, and has a fair amount of usage in Node (https://github.com/joyent/node/wiki/modules#wiki-middleware-...), as well as other server-side JavaScript platforms (Ringo, Akshell, Pintura, Narwhal)
I'd love to hear the reasoning for the differences between the two.
JSGI is not able to take advantage of node's ability to serve responses asynchronously because the spec doesn't allow for it. JSGI specifies that the return value of an app must be the [status, headers, body], whereas Strata specifies that the return value of an app is not important and that the app's callback should be used to serve the response instead.
My JSGI implementation in Common Node (https://github.com/olegp/common-node) uses node-fibers allowing it to stream out responses without blocking the event loop.
In fact, by connecting an input stream to an output stream (forEachable objects), the underlying code automatically enables Node's pipe like behavior.
Even so, this breaks down with JSGI middleware that depends on running in a Java environment. For example, JSGI's gzip middleware uses Java's byte buffers.
Which JSGI gzip middleware are you referring to? It should use portable CommonJS Binary objects, which in Common Node's case wraps Buffers and in RingoJS (Java) byte arrays.
Kris Zyp's "jsgi-node" absolutely does support streaming. As does Kris Kowal's "q-http" and "jaque" framework.
There have been lots of discussion on asynchronous/streaming in JSGI, but perhaps it hasn't made it's way into the specs you've looked at. Promises are one way, fibers are another.
Foreach-able "streams" w/ promises and Node streams are pretty much equivalent (and yes, JSGI w/ promises can handle back pressure, though I'm not sure if there are any public implementations yet)
I find it similar to connect/express http://expressjs.com/guide.html but a less tasty DSL and nothing new, or am I missing something? I'm sure one inspired the other since the API is strikingly similar (app.use app.get etc), strange that no comparison is given. Anyway, kudos to the devs for free software!
Having worked with Node for a while now, I can conclusively say it's difficult to say you know it well without building your own framework. Even if you end up with something similar to another framework it's still worthwhile doing just for the educational aspect.
57 comments
[ 3.3 ms ] story [ 135 ms ] threadtjholowaychuk has done extensive testing/benchmarking of Connect/Express. I'd really like to see some benchmarks and code coverage.
Strata gives you a sane environment modeled after the WSGI/Rack/CGI model, which has been the backbone of web servers for years.
Also, Strata's API matches the WSGI/Rack model. This may seem small, but in practice it's a significant benefit.
Add to that the list of features cited in the release email, including native support for streams in node 0.4.
From what I understand, a Node/V8 instance really only utilizes a single CPU, yes? As a request does I/O, some other piece of code is executed until (some time after) the async I/O completes. A CPU can be effectively timesharing over several pseudo-threads, but to use a SMP chip/server, you have to run multiple instances, with a front-end load balancer. I guess this would drive one towards an external session data solution, barring some kind of sticky session and a reason to be sticky.
I'm no fan of Node, but even if you have $100k machine running the JVM with a thousand threads you will (if successful) eventually need a second machine, and a third, etc. Not to mention persistence (what if you need to restart the app server?).
Sessions belong in a database of some kind unless you can get away with signed cookies.
http://playframework.org/
for java and scala
After seeing how awesome the default cookie-based sessions are in Rails, I'm not sure I'd ever care to go back. Maybe for a high security banking app where breaking the cookie encryption could have catastrophic, permanent repercussions? Short of that, load balancing shared-nothing cookie-based sessions is a joy. Gotta agree with the Lift guys there.
http://www.quora.com/What-are-the-advantages-and-disadvantag...
"In practice, scaling a Lift site is much much easier than scaling a LAMP site. Why? Well, state exists someplace. If it exists in the JVM, you get a lot of performance benefits and stability as well as, in Lift's case, lots of security. Contrast that with sessions in memcached. "Whoop, memcached went down, there go a pile of sessions." "Whoops, we've got a new memcached hashing algorithm, there go all the session." "Whoops, Google just crawled us creating 200,000 new sessions pushing all the but the active sessions out of cache." "Whoops, the Ruby runtime just went wild, ate all the VM on one of our boxes, memcached went down..." So, you try storing sessions in some wacky shared version of MySQL. This solution requires tons of hardware and a team of make sure that the sharing code is correct, etc. Contrast that to using Nginx, Jetty and session affinity. It's about 4 hours of setup time and it just works. See http://blog.harryh.org/post/7550... So, talk to a Facebook engineer about the challenges they go through to manage state between the front end, memcached, MySQL, etc. Compare that to Twitter with the famous fail whale. Compare that to Apple's store and the iTunes store which are written on WebObject (which is highly stateful.) Lift apps running at scale typically require 7% of the front end resources of LAMP app. The Lift apps that are running at scale (Foursquare and Novell pulse are two) do not have the kind of scaling issues associated with LAMP sites that have similar traffic patterns. Scaling with Lift is neither tricky, nor risky. It's simple. It's known. It's proven. Scaling with LAMP is playing whack-a-mole with state and that only becomes a problem at scale."
Sessions are just about the easiest thing to scale. What am I missing here?
Really though, everything shown can be easily replicated with a bit of Connect middleware, and it'd be cleaner. I don't see the point?
And it's much nicer to use than the http module: layerable middleware, streaming responses, routing...
What I don't get, is you have to fight with Rack to get anything close to async, yet here comes a web framework for an async environment modeled after Rack, go figure.
I assume (meaning I'm about to make an ass out of u & an ass out of me) the answer is to pass a stream in place of the "Hello world!" string, but that would cause hell with any middleware expecting a completed response.
Strata borrows many concepts from Rack, but not the synchronous part. Instead of expecting your app to return something, Strata gives you a callback that you can use to serve the response when you're ready.
I admit the manual could probably give a better example of streaming a response. For now, check out the static file serving middleware (https://github.com/mjijackson/strata/tree/master/lib/static....). You'll see that the response body is simply a readable stream to the file on disk.
For an example of using ejs with Strata, take a look at the source to the Strata website: https://github.com/mjijackson/stratajs.org
https://github.com/LockerProject/Locker/blob/master/Ops/webs...
And tends to end with around 40 lines of checking URLs and other various bits of information to know which middleware can be chained in, and in which order that doesn't cause them to conflict with one another.
If Strata can solve this mess, then it's definitely a step up in my opinion.
The Server header reports nginx because it's sitting behind a reverse proxy on Heroku.
I'd love to hear the reasoning for the differences between the two.
(Disclaimer: I started JSGI)
This is an API incompatibility that cannot be ignored. The most developed lib I've seen up to this point to get JSGI working on node (https://github.com/kriszyp/jsgi-node) isn't able to get asynchronous responses working properly. Instead, it just does a body.forEach (see https://github.com/kriszyp/jsgi-node/blob/master/lib/jsgi-no...) to write each piece out to the response synchronously.
Copied and pasted from https://github.com/mjijackson/strata/issues/2.
In fact, by connecting an input stream to an output stream (forEachable objects), the underlying code automatically enables Node's pipe like behavior.
Check out this example: https://github.com/olegp/common-node/blob/master/examples/ht...
There have been lots of discussion on asynchronous/streaming in JSGI, but perhaps it hasn't made it's way into the specs you've looked at. Promises are one way, fibers are another.
Foreach-able "streams" w/ promises and Node streams are pretty much equivalent (and yes, JSGI w/ promises can handle back pressure, though I'm not sure if there are any public implementations yet)