 So I'm going to be talking about HTTP. And I saw the show of hands earlier of how many people write web in Ruby. And I just wanted to see how many people have actually read the HTTP spec. All right, more than I expected, maybe about a quarter or so. But if you're writing web apps, you pretty much, you should read it. I mean, here it is printed. You know, it's about 100 pages. It doesn't take very long to read. It's not dry like most specs. You know, it's an RFC. It's pretty good reading. It's really interesting, at least to me, but I'm a pretty big nerd. So it started out as just a document transfer protocol, but over the last, well, starting about 10 years ago, it became a full-featured application protocol with lots of features, and it really helped out with the performance of the whole thing. So I'll be talking about some of the important features that you can use to make your web app faster and make the whole thing, so when you're designing your app, so the whole thing can be even more scalable. And I'm mostly going to focus on the server side because that's what we have control over. We don't really get to play with how the browsers manage their connections and everything. But browsers have really, really good HTTP client implementations. I mean, even the old ones, like IE4. It supported all of this, caching and everything. So that's me. You can find my code on GitHub there. I've got several libraries and Twitter and email and my crummy blog. And I work at a company called Absolute Performance. We do hardware and application monitoring for all kinds of clients, and most of our clients are large data centers, and we monitor, make sure all their servers and all their hardware is up and running, and all their clients' web applications are running. And in one of our data centers, we have a cluster of machines that serve about 150,000 HTTP requests every minute. And that's with just five application servers and a load balancer and a database node. So it's not a whole lot of hardware because we were able to take advantage of a lot of what HTTP can offer to pretty much prevent most of the requests that we would have to answer. So a little bit of history about HTTP. It came about in 1990 when Tim Berners-Lee proposed the World Wide Web of hyperlink documents, and they were delivered over the Hypertext Transfer Protocol, which was later named HTTP 0.9. And that's an example of what 0.9, if you wanted to get an old 1990 version of Darth Vader wearing a sombrero. And so then until 96, the development of HTTP was pretty ad hoc with just vendors of servers and clients just implementing whatever features they felt like they needed. And it was just, it was pretty hodgepodge. And then starting about 96, they put together another standards to add, to formalize these features like request headers and they added a few more methods like head and post. And so here you can see like it's got the, it's got a request header and the server actually responds with headers about what this thing is instead of just the document now. And we get a little bit better, bigger quality of Vader wearing a sombrero. So then they formalized HTTP 1.0 in RFC 1045. And then at the exact same time they began work on the first draft of 1.1, which became RFC 2068. And it included just beginnings of all the features that we have now, like persistent connections and hierarchical proxies and caching and virtual hosts. And so that's an example of what it looks like now and that's not everything. But it was finalized in 1999, 11 years ago now, as RFC 2616. And these features allowed it become turning from a transfer protocol into a full-on application protocol. And we get, you know, nice high-res pictures of Darth Vader and a sombrero. And so this is the spec, you know, that's how big it is. You know, it's 60,000 words. It's about the size of a small book. You know, you spend a week reading it in your spare time. It doesn't take very long. There's a lot of really good information in there. And it's not, I mean, it's a little dry, but it's not like an XML spec where it's a million pages of junk. It's actually interesting and it's well-organized and it's pretty descriptive of the whole thing. So some of the features that HTTP.1 has that you can take advantage of to make performance better are the persistent connections, which is also known as pipelining or keep alive. And you can cache responses that you have previously and serve them again without having to regenerate the whole document. And you can use hierarchical proxies like a load balancer or a caching proxy. And so a persistent connection avoids the TCP IP overhead of having to establish a connection every single request. And, you know, most pages have lots of CSS, lots of JavaScript, lots of images all embedded into them. And if you have to build and tear down a TCP connection for every one, that's quite a bit of overhead. And, you know, this is just built into Apache and Nginx. Like, if you're using it to serve your static assets, you just turn it on. I think they're probably on by default. So, you know, I think it's keep alive on in Apache. So make sure that's set and it'll just happen and all the browsers support this automatically. And so that's, this is what it looks like with a non-persistent HTTP request. So, first you have to do the DNS lookup and you get a response. And then you have to do the TCP SINAC, you know, that's a three-way handshake to make sure the connection's ready. And finally, you can do the get and you get the response. And then when that's all done, you have to do a four-way handshake to finalize and close the connection. And then when you need to get the next thing, you get to start all over and do it again. And you skip the DNS request. But still, it's a lot of overhead. But if you're using persistent connections, you get to skip the three-way handshake and the finalize between every request. So I just have two here, but you can do a dozen, you know, if all your JavaScripts are coming from the same server. And that, you know, there's less lines, so it's faster. Right? So the big one, the complicated one, I guess, that it's tricky to think about and tricky to get set up right. And that's using HTTP for caching. So there's two goals to caching. One is, you know, don't make a request at all. Like, if you just got something and it hasn't changed, you don't have to get it again. Because you already know, and if you have it cached locally, that's faster than all that HTTP stuff. And then another, the next thing is validation, where you have it, but it's been a while. But you can just check, has this changed? And the server can answer pretty quick, you know, no, it hasn't. Or yes, it hasn't give you the full thing. But very rarely, compared to the number of requests you're getting, have any of your documents actually changed. So that was that slide. So there were, there are only two hard things in computer science, caching validation and naming things. And the cache naming thing, I spend 40% of my time trying to think of what to name something. But caching validation, like, that you have caches all along the line that you're not in control of, you know, how do you decide when something isn't good anymore? You know, somebody, one person updates a blog post or adds a comment, and that somebody else is looking at the blog post. When do you decide that they need to get that comment, or that now there is a comment, so you need to invalidate the cache. And it gets pretty complicated when you have, you know, multiple things all talking to, through different caches all at the same time. So, but when talking about caching, it's easier, we need to make sure we get our terminology right. So there are HTTP clients, and there's HTTP servers, and a client is anything that can connect to an HTTP server, and a server there's anything that can accept connections. It's pretty bland and generic. But then there are certain ones of these are special. So the user agent is a client where the request originated. It's, you know, usually a browser or a spider end user tool of some sort. And it's, you know, it's the start of the whole thing. And then the, the origin server is at the other end, it's the server where the resource lives, or where you're going to create it. It's the authoritative source for a document. And then in between you can have proxies, which behave as both a client and a server. They can, they can service the request themselves, like if they have it in their own internal cache, they can just provide it real quick. Or, or they know where to forward it to, to get to the, the, you know, another proxy or the origin server to get the, the actual authoritative answer. So the HTTP 1.1 provides a bunch of headers that you can use to manipulate how the user agent is going to cache and how all any intermediate caches are going to manage their cache. And you can manage the first thing like expiration, like when do you know what you have is still good and when do I have to go check again using the expires and the cache control max age directive. And you can manage validation like checking to see is what I have still good. You can check, set that with the last modified and the e tag and the vary headers. And then the cache control header itself has a dozen options telling intermediate caches what they can and cannot cache and how long and what to do with it and telling the private caches which belong in the user agents how they can manage their cache. So the server does have a great deal of control. So the, the expires header and the, the cache control max age directive. So the, that's when the, when the document, the expires header sets a date when the document should be considered stale and which you shouldn't validate. So it's going to have a, it's going to have just a date that looks like a regular HTTP date after which it doesn't, it's, you should go check again and revalidate. And to set things as already expired you set it equal to now and to make things considered that they're never expired, which like what, if you use Google's API hosting of jQuery or prototype, you know, that they set theirs to a year from now. And so a browser will come and get that and it'll hold on to it for a year. And so you can save downloading 40K ever again, once a year. But probably reinstall your browser by then or delete your cache or something. Oh, look, I had a slide for that. So this is an example of what the expires header looks like. It's, so yeah, I'm getting the Ajax live from Google of the jQuery 141. And it comes back with the, I have it in red there. That's what the expire header looks like. And it's a year from three days ago when I got that. So, yeah, a browser isn't going to get this for another year. And since this, you can use this in any web app. And lots of web apps are using this now. So even though, you know, the first person somebody comes to my site, they may even already have this cache from visiting another site four months ago. That's going to save them from having to load jQuery. And Google hosts, you know, a couple, like a dozen now, of prototype and jQuery UI and Mootools and all kinds of, and they have it all, all the latest versions. This is how you can set it yourself in, in Rails. I tried to say, I thought they had a helper for it, but they don't. But you can just set the expires header manually. And here I set it to a year from now. And so the, the other one is MaxAge. It does pretty much the same thing, except instead of a date, it's, it sets the number of seconds that this thing should live. And yeah, that's what it looks like there. So MaxAge is, you know, 31 million seconds, which I think is a year, probably. And Rails does have a helper for this. So you can explicitly set when to expire in so many seconds. So then validation using the, so last modified, the, the origin server can indicate when it believes the resource was last modified. It doesn't have to be exact. But sometimes you can use like the last updated column in your database. And that, that's a, or like Apache uses the file m time. And you can use a date, there, yeah, a DB timestamp. And so then, once a user agent gets that, the, you can use the if modified sense header, which the browsers do, and it makes the request conditional. So it just, it gets that time that it was last modified. It sets it in a header called if modified sense and makes the request. And, and the server itself, or an intermediate cache can know, no, this hasn't been modified. Here's a 304, which is the response code for not modified. And that just means you're done. And you don't have to regenerate the response. And it saves you a lot of time. So here's, we get a request and the server comes back. That's the last modified header. And then when the browser makes the request again, it asks with modified sense and it can just respond, no, this hasn't been modified. And that's it. The connection's done. And that takes, you know, milliseconds instead of hundreds of milliseconds. And the, the e tag works very similarly. The, the, the e tag is another header, but it's, it's just an opaque string. You can put whatever you want there as long as the e tag changes every time the document changes. And Rails defaults to just an MD5 of the body of the response. So that's going to change anytime that response body is different. And so then the user agent can set if none match with the value of the e tag. And it'll happen the same way with the 304. So here's an example, e tag. That's, I think that's an MD5 sum. And you just set it if none match. And you get the 304 not modified. And so Rails, like if you can circumvent this and do this yourself. You know, you, you have some smart way. You can even put a time stamp in there because it's just a random opaque string that the meaning of the string doesn't mean anything. But the way Rails does it is it has to generate the entire response and then MD5 the body and then it can come back with 304. So it saves you bandwidth, but it doesn't save you any application server time. Because you still have to generate the entire response. But with using the, uh, yeah. So here's, you can set in Rails when a response is fresh. And it's when the e tag matches this. It's when the last modified matches this. And the public just sets another one of the cache control headers that I'll talk about in a minute. And, oh yeah. So here's how you can, so say rendering, like you're using something complicated. It takes a very long time to render. Rails has a helper where you can check if something is stale. So here we're, we're using the updated at column of the, the sombreros object to, like getting the absolute last time this thing was modified. And then we're checking to see if the, the client's last modified timestamp is matching this. And if, if it matches, then we're done. We're just 304. And if we're not, then we gotta go find millions of sombreros and return them. And that's, that's a long time. And by using the if modified sense, and it doesn't change, especially if this doesn't change very often, then you save your app server. So then there's another one, you can set the very header and that's, it's a list of request header fields that you should change on. So like if you're, you're varying on authentication, like you have a cookie that somebody's off and there's an intermediate cache server, you get a response for one person and somebody else asks and the server says, oh wait, I already have that cached. Here you go. Then somebody just got the private interface, private information intended for somebody else. And, and that could be bad. And there, there's other things you can vary on, like some clients would accept XML and somewhat JSON and someone HTML. And you don't want your cache serving the wrong thing to the wrong person. And so you list the accept header in, in very and the cache knows that unless they're the same, it can't use the previously cached response. So then the, the cache control header, it, it has several directives that you can list inside it. So you can set, set something as private. And that means that any intermediate proxy caches aren't allowed to cache this, but the user agent is still allowed to cache it. And you would use that for something that might contain sensitive data that, that a cache shouldn't hold on to. And the cache will have to, you know, forward the request on to the server every single time somebody makes that request. And then you can set public. And that, that's usually the default. And that means every cache along the way is allowed to hold on to this and keep it and use it. And then there's also no cache, which means the caches in between can hold it and store it. But every time somebody asks, they have to revalidate it with the origin server using that, the, the if modified sensor, the e tag or one of those headers. And then there's also the no store directive, which means like, you can cache it, but don't ever store it to disk or some sort of long term storage. And you use that to protect like medical documents that you might want to look several times. But you need, you don't want them getting backed up to tape for the next 20 years. Something's out of order. Whoops. All right. So there's an example of what the, the very header. So here we're varying on except in several other headers. Then here's, okay, here's what Firefox actually sends. And this isn't all that it sends, but it's a lot of them involved in, in caching. So it, it automatically provides the if modified sense and the if none match. It handles all that. And these things, so except in sec, except encoding and the cookie. So there's a session key in that cookie. That all needs to be in the very header, because you don't want somebody getting something for somebody else's session. You don't want somebody getting XML when they wanted HTML. And you can also like compress the documents on the fly. And a user agent can say whether or not it understands that. So you don't want somebody that doesn't understand GZIP getting a GZIP document because then they just get line noise. Then this cache control zero coming from the client, it, it forces intermediate caches to revalidate. So then there's a, there's proxies that, you know, that they can handle requests intermediately and they can forward them on or they can respond. There's, there's different types. There's the load balancing one, which can, it'll take requests and it'll hand it off to the server that, that is least busy or there, there's a variety of algorithms. It'll just go in order. And there's ones like depending on how deep the queue is on each. And there's a, they use smart algorithms to determine like which server is busy. Like he was talking about, HA proxy is one of these. And it can also detect when a server is dead and not use that one and just keep checking until it is. And then there's a, a caching proxy and it can, it'll store the caches or store previous responses in memory and hold on to them so that your application server doesn't even know that somebody made this request and it, it was free. It didn't have to do anything at all. And somebody was able to answer on behalf of the server. And it, it, so all those validation rules we were talking about the like squid and varnish are caching proxies that respect these rules. And the, the, the cache is also able to re-validate cache responses with an origin server. So, I, I wasn't sure if I wanted to get into this because this is, this is a pretty huge complicated topic. But I'm going to spend a little bit of time talking about just how you can take advantage of some of the principles of rest in your application design so that things like caching and everything work a whole lot better. It's, so it was, he asked, is rest part of the HTTP spec and, and no it's not, but it was written by one of the authors of the HTTP spec at the exact same time he was writing it. It was by Roy Fielding and he wrote it for his doctoral dissertation. And it's just some of, it's not even like a spec or anything. It's just some guidelines like, like here are some ideas, some good ideas to take advantage of HTTP. And so it's not really codified anywhere except for a few paragraphs in his dissertation and it's not even an architecture or a framework or it's, it's just like, here's some ideas. You know, you might want to think about doing things like this. And if you do, like you can take it, like you can tell when you're reading through the spec and the rest paper that they were come up with at the same time and there's a lot of ideas that are in both. And it's just rest, like HTTP is like, here's a document protocol, you know, use it however you want. And rest is like, here's how you should use it. This, this will make things a lot easier for you. So the main thing about rest is this unpronounceable acronym, which means the hypertext is the engine of application state. And that means that instead of like storing user data and stuff in session, like Rails makes it really easy to just throw data in the session and use that later and just keep track of it. And the, the point is here, instead of that, like use links and use resources to keep track of where a user is and what they should do next. So that I had an example of that. But yeah, so, so a resource, like, so when you link between resource, you know, one resource is one state. The next resource is the next state rather than having one big spot and you just do things there, you send and receive and you store everything that, that has been going on in a cookie. So a resource is anything that's identified by a URI and the names are like, so it's a univeral, universal resource identifier. And a resource is something that, that you can do an HTTP request to. It's, it's a location. So you can get it and you can post it and you can, it can have multiple representations. So, so the same thing can live under HTML or XML. So like you can have your blog post with a list of comments or you can have an atom feed in XML with the same list of comments and they all live at the same resource because they, they're the same thing. They represent the same thing. So then how does that matter with caching? And so a proper cache treats each resource separately. And so, so things that happen like a, a post or a put to a resource will invalidate every cache along the way. So they know that what I had before has probably changed. So the next time somebody asks me for it, I should look again and make sure what I have is right and if it's not right, I should update it. But if you're using, if using a session, so if there's some data in the cookie that is being used to render this page, then the cookie or a user session in the database, like that, the cache isn't going to know that that's changed. So you go get a resource and it does something behind the scenes and it updates a cookie with like a flash message like, hey, I got what you said. Your form is saved. And a cache is going to save that. And then the next time somebody comes along and sees that, they're going to say, your form was going to be saved. And they didn't do anything at all with a form. They have no idea what that message was about. So if you do something, instead of storing a session, like if you save the form using an Ajax and use that to update the page, the page never changed. The cache never noticed. And anybody coming along is going to get the exact same thing every single time. And your app server will be much happier. And that was what I just talked about. So when you're using a cookie for an auth, which is what everybody does, like you store, log in, it sets a cookie and that's who you are. You need to make sure in your very header you list that cookie. Because otherwise, like I said, you're going to get, somebody's going to get responses for somebody else. And so the other thing is if you're storing the session in the cookie, then every time somebody does something, that cookie is going to change every single time. And you're going to vary on it and you're going to get nothing cached ever, because the cookie changed every time. And the cache is going to say, well, what I have is no good because the cookie changed. And then the other one was if the page contents are based what's stored in a session somewhere, the cache is going to have the wrong thing cached and it's going to provide an inaccurate response. That's all I had. So you can get my talk here. I was using Showoff, which you get to write your slides in Markdown. It's pretty slick. I really like that a lot. But anyway, the source is available on my GitHub. And then these three links right here. So go get the source and read those things. And that tells you everything you need to know about caching. Pretty much everything that was in this talk is in those three papers right there. And then the last one that's so the spec has like a dozen pages devoted to here's how you do caching in HTTP. And it's all really good information. And anybody have any questions? Plenty of time. Questions? So if you use cache control private, you can't cache anything. Like the user agent can. But if you have you don't so when you set up like you have a dozen application servers and you have varnish in front, if you say private, you don't have like that cache is never going to store anything. Yeah. Yeah, but it's probably a good idea to do anyway because it's easy. And if you, you know, your Twitter and your load explodes, then you need to know we'll throw a proxy server in front. And that'll make all our problems go away. I would think if so if you're using cookies for auth, yes. If you're not, then it doesn't matter. Yeah, probably. So you could cache it. So I think Rails default is fine for what it is. I might, you might want to consider if somebody turns on session or doesn't, or fails to turn off session. Like check session, if session is on include cookie in the very header. That seems like it would be a reasonable default. But what it does now is probably acceptable. But that's the other thing I wanted to point out is Rails does, makes a lot of defaults that do the right thing in every case for the worst case behavior. Like, you know, it doesn't disable caching, but it makes it, it's very, very, you know, weak about what it does. And so you need to tweak some settings in the, the config, like do what I did. I showed with the, the stale and the fresh win to make Rails not be quite as dumb when you want, when you know for a fact, you need to be caching these things. He asked if I'm familiar with shared dictionary compression over HTTP. I've, I've, I've heard of it, but I'm not familiar with it. What is it? Oh, okay. And, and I'm not sure you would have to have control over the client. Right. And most browsers don't support that. So you'd have to like do that in JavaScript or something. Okay. Yeah. So, so if you, if you're writing a client, or if you're using one like REST Client or one of the Ruby ones, then like none of them do caching at all. And you'll, you'll either have to handle that yourself or on my GitHub I have a, Peter Williams and I wrote a library that we used internally at absolute performance to, it handles all the caching for you. It's got its own internal cache and it can store it, you know, either on disk or in heap or I think it, I think we can do memcache, but we aren't using that one. But it's, it follows the spec and so, you know, that's one of the things that saved us a lot of the requests so we can handle the load that we do. We, yeah. So I, we've evaluated HA proxy and varnish. We haven't, we haven't installed any of them yet because it's a huge pain to get somebody at their data center to go, hey, we got this new piece of software, install it please. And I'm like, wait, wait, we got a QA this. It's going to take three months. So we're not actually using it now, but we haven't, we haven't needed it because we, we handle all this with caching on the app and we, we tweaked rails like most of the long-running controller actions, if they haven't changed it to boards early and doesn't hit the database, it doesn't hit the view code. And so we, we haven't actually needed it, but I think we're coming up now, like one of our data centers, they're about to turn on a few thousand more monitoring. So I think we're going to have to do something, but I don't know how we're going to get that in. Is that it? All right. Thanks. Thanks.