7 ms·
Actix: a small, pragmatic, and fast Rust web framework
- berkus 8y agoI started porting one of rocket-based synchronous services to actix-web and so far I'm pleased with the process.
- Dowwie 8y agoActix-web is great! I've adopted it for my projects. I can take full advantage of what Rust offers for concurrency and safety without going too deep into the weeds. Documentation, a growing number of examples, a very responsive author, and growing community are some of the reasons why I think this project is going to play a major role in Rust's web development story going forward.
- fafhrd91 8y agorecent TechEmpower benchmarks results Citrine: https://www.techempower.com/benchmarks/#section=test&runid=60fab9eb-a5ad-49cb-aefe-24ad0f377122&hw=ph&test=plaintext https://www.techempower.com/benchmarks/#section=test&runid=6... Azure: https://www.techempower.com/benchmarks/#section=test&runid=b837bda0-fb68-4a98-9b25-eaad92039cea&hw=ph&test=fortune https://www.techempower.com/benchmarks/#section=test&runid=b...
- stusmall 8y agoWhat a great looking site. I've been really exciting about actix for a while now. We just started some internal experiments with it here and I'm looking forward to more.
- topspin 8y agoAgreed. Good site. This is an aside, and I sincerely apologize for that, but I am compelled... I'm reading the greeting/hello-world example on this nice site and I notice unwrap_or(). That is a poor name: can it panic, as suggested by the "unwrap" part (I have just enough Rust to know that,) or can it not, as suggested by the "or" part? The name is inherently ambiguous! It's as if the .unwrap() that is festooned throughout such example Rust code has become so ubiquitous that someone felt it had to be used and so tacked on "_or". Why couldn't it just be .or() or perhaps .default()? And so I investigate and things go rapidly downhill from there. Consider: unwrap unwrap_or unwrap_or_else unwrap_or_default or or_else Good grief. The word ambiguous seems inadequate to describe what has emerged here. Again I'm sorry; this is clearly off topic, probably badly naive and possibly inappropriate in a few other ways to which I'm pathetically oblivious. I couldn't help myself.
- steveklabnik 8y ago"unwrap" doesn't mean "panic". It means "to take out of some kind of container". So, the question is, what to do if the thing isn't in the container? unwrap: panic unwrap_or: produce this value instead unwrap_or_else: produce a value by running this closure instead unwrap_or_default: produce a default value instead or and or_else are just like unwrap_or and unwrap_or_else, but they don't do the unwrapping; you keep the container. In this case, the "container" is option, but similar types have the same methods, like Result. > Why couldn't it just be .or() or perhaps .default()? or returns Option<T>, unwrap_or returns T. .default returns the default value for a T already. TL;DR: you have a lot of options (pun intended, sorry!) with what to do, but the names all follow a quite regular scheme.
- dbaupp 8y agoThe "unwrap" is referring to getting a plain T out of a Foo<T> container: all of those "unwrap" functions return a plain T. One way to look at them is "unwrap", "or" and "or_else" are building blocks that have a common meaning across the different examples: - unwrap: returns a plain T - or: the left-hand side, unless it is a "failure", then use the argument value - or_else: the left-hand side, unless it is a "failure", then use the argument function to create the value
- malcolmgreaves 8y agoLearn about the Option type: https://en.wikipedia.org/wiki/Option_type https://en.wikipedia.org/wiki/Option_type
- masklinn 8y agoFor prefixes, unwrap mean Option<T> -> T while or means Option<T> -> Option<T>[0]. For suffixes, "else" means executable code (a closure). > > It's as if the .unwrap() that is festooned throughout such example Rust code has become so ubiquitous that someone felt it had to be used and so tacked on "_or". Why couldn't it just be .or() or perhaps .default()? Because Rust doesn't have function overloading and thus you'd be missing most of the cases? [0] or more generally Wrapper<T> -> T and Wrapper<T> -> Wrapper<T>
- 8y ago
- deft 8y agoLooks nice, really great landing page too. Informative and concise, didn't leave me asking "what is this?"
- LambdaComplex 8y agoCan someone explain to me what advantage the actor model provides for a web server?
- Diggsey 8y agoActors are generally a very powerful abstraction. To give a specific example: they can be used to mediate access to a resource without requiring complex synchronisation: instead of sharing a piece of mutable state (like a cache) and protecting it with a lock, you can ensure a single actor accesses the cache, and other code communicates with that actor. This is particularly useful with asynchronous code, because it's not possible to have a fair, asynchronous "passive" mutex without suffering from the thundering-herd problem. If you try to implement such a mutex, you will find yourself needing to queue up lock requests and responses, and you will end up reinventing the concept of an actor.
- the_mitsuhiko 8y agoWe use it at Sentry for one of our services and the experience has been great. The best part by far is that you can benefit from async io handling without having to write every one of your views in an async fashion. All the async complexity is offloaded into the extractors and the response sending.
- ovao 8y agoIs this a new service built from scratch in Actix, or did you migrate the service from something else?
- the_mitsuhiko 8y agoNew service entirely.
- kibwen 8y agoDo you have any performance numbers, or a prior version of this service to compare it to? I'm curious how async in Rust compares to synchronous code in real-world applications, especially given its yet-incomplete state.
- the_mitsuhiko 8y agoAbsolutely no idea since we did not replace an existing service. From everything I have seen from tests performance is not a concern here and our bottlenecks are elsewhere entirely.
- kibwen 8y agoIt appears that Actix's primary author works for Microsoft, does anyone know if Microsoft is using it internally for anything?
- fafhrd91 8y agowe use actix. but that is all i can share :)
- kibwen 8y agoHave you spoken with the Rust core team about Microsoft's use of actix? They love getting feedback from commercial users, and I believe they are willing to sign NDAs when necessary (there's certainly plenty of commercial users they seem to be unable to tell me about, and I ask often :P ). I'm happy to put you in touch with them if you'd like; see my email in my HN profile (and this invitation goes for anyone else out there using Rust in production capacities, of course!).
- tuananh 8y agoHe's the author of actix
- cies 8y agoSo MSFT uses Rust. :) That's actually quite a newsworthy thing, could be a lot more newsworthy if we knew the purpose it was used for was some mission critical component that also needs to be blazing fast. Looking at Rust's strengths, and that MSFT has languages/compilers of it's own, the use case is prolly "mission critical component that also needs to be blazing fast". But for now we're guessing.
- infynyxx2 8y agoBrowsing thru Actix guide (https://actix.rs/actix/guide/ https://actix.rs/actix/guide/), I didn't find any explanation regarding what will happen when actor(s) crashes or how crashes[1] are being handled? http://wiki.c2.com/?LetItCrash http://wiki.c2.com/?LetItCrash
- fafhrd91 8y agofirst, you need to define what is crash in rust means. panic or error. in general case you can not recover from panic. in case of error, type system prevents unhanded errors in actors. you can restart actor, but that is controlled by developer action.
- lewisinc 8y agoI feel like Let It Crash and Fail Fast make a lot of sense in a dynamically typed language where, if you were to go ahead and code defensively and try to make assumptions about where your program could possible fail, you'll end up with a bunch of redundant error code and will probably have written error handling for places where the program might not have ever crashed. Rust's Result type makes sections where an error can occur pretty explicit, so I'm not sure it makes sense to follow the same methodology. I'm interested to hear what other people think.
- nevi-me 8y agoI've had relatively good success moving some microservices from Kotlin to Rust (mainly saving about 90% resident memory util). I picked up Actix recently, and so far I'm enjoying using it. If Rust library support for geospatial tools was as good as turf.js, I'd be able to move a lot more stuff into Rust.
- MrBuddyCasino 8y agoWhats your driver? Do you save enough server resources do make it worth it, or is it just a hobby project?
- nevi-me 8y agoI run a public transport website that includes a few mobile apps. I've broken it down into quite a few microservices, but the bulk of it runs on Node and JVM. I have a 64GB RAM, 12 core server, and the JVM services take up about 25% of resident RAM (ignoring Kafka and other Java stuff). I've wanted to learn Rust for a while, so I recently bit the bullet. I use gRPC everywhere (Dart/Flutter, Node, JVM, Python), so I decided to start by rewriting some small services in Rust using gRPC for comms. For now, I've taken a Java service that used 500MB at peak, to under 10MB RAM. I'm planning on eating into the big stuff over the coming months. It's not making money, so it's a "hobby" yes. Consulting's paying the bills so I don't mind at this point. EDIT: E.g. here's an url shortener gRPC server that runs on NodeJS, and a Rust client that can shorten urls and get results back. https://github.com/MovingGauteng/shorty https://github.com/MovingGauteng/shorty https://github.com/MovingGauteng/rust-grpc-shorty https://github.com/MovingGauteng/rust-grpc-shorty
- MrBuddyCasino 8y agoThanks, situation is similar for me - a 5€ Digital Ocean Droplet (1GB RAM) goes much farther with Rust services than JVM based ones. FYI: https://nevi.me/ https://nevi.me/ errors out with a 502.
- nevi-me 8y ago
- zarvox 8y agoI've recently been building an IRC bouncer and webapp with Actix, and it's been really smooth sailing so far -- excellent documentation, extensive examples, and everything I've touched so far has just worked the way you'd expect it to. It's a gem of a project.
- MrBuddyCasino 8y agoWanted to do the same exact thing! Is it public?
- zarvox 8y agoNot yet (still incomplete) but I'd be happy to drop you a line if/when that changes - send me an email?
- jsandler18 8y agoI tried using actix for a recent project. I just could not get it to do what I wanted to. It always felt like a fight. I switched to rocket and everything is so much easier. Everyone is saying great things about it, but I just want to point out that it's not for everyone.
- tormeh 8y agoDon't know anything about Actix, but can confirm that Rocket is excellent. The actor abstraction is very interesting, though. Anyone have any insight into how Actix and Rocket compare? I'm interested mostly in ergonomics and safety.
- fafhrd91 8y agoi think from ergonomics standpoint, actix is very close to rocket. of course rocket has some advantage, but actix compiles on stable and has zero codegen code. as soon as proc macro stabilizes both will be on par. from performance perspective, actix is faster than rocket on any type of load.
- jdright 8y agoI've done a simple hobby project in Rocket and ported to Actix. I'm not experienced with web services and my project was very limited for learning purposes, here my take away: I like both and for me Rocket was way more ergonomic for creating routes dealing as if they're simple functions where input and output are dealt automatically with (from request and to response). Actix advantage is actors and is easy to be fully async, I had some issues dealing with it but most of my troubles were extracting request data and building responses. When actix-web will support magic as Rocket (once proc-macros becomes stable) then actix-web will have the edge if Rocket don't become async-ready and stable before. For both it is only question of missing stable rust features and actix-web is already running on stable. actix-web: - easy - async - stable Rocket: - stupidly easy - sync - nightly
- snake_case 8y agoI also recently just finished porting a side project of my own from Rocket to Actix. I'm absolutely loving Actix so far! Other than Rocket being nightly, the other reason I switched to Actix was because Rocket doesn't have the ability to respond to requests directly within the middleware layer, you can only modify the response but not return early. This is pretty important with regards to CORS and trying to catch all OPTIONS requests. There are a few solutions of course, but all of them felt hacky or verbose. I have no complaints with Actix yet.
- SloopJon 8y agoI came across this the other day while looking into Flow, the C++ extension used to write FoundationDB. This Github issue asks about a benchmark on which Flow claims really good results: https://github.com/actix/actix/issues/52 https://github.com/actix/actix/issues/52 Actix does pretty well too.
- reacharavindh 8y agoI have just started playing with Actix-web. Rust-Noob as well. The yellow world example compiled to a binary that was ~ 5 MB. As a general case, Actix-web pulls in a lot of dependencies at install, and compile time.Are all of those dependencies really necessary for a hello world scenario? Being a Rust newbie, I thought maybe I was using the wrong tool and started to look at hyper instead..
- sondr3 8y ago5MB is nothing compared to what you'd use for a similar project in node or Python or Ruby. Sure, it's not the tiniest it could be, but using tools like strip, not including debug symbols etc it gets pretty damn small. Honestly though, at that point I think size becomes entirely pointless to even mention unless you need to run it in a super constrained environment, which you're probably not when you're using the standard library :)
- reacharavindh 8y agoYes, I'm not constrained in any way to not be able to run a 5 MB binary. I was curious if that was an indication of what is to come for large projects... I guess I was tangentially pointing to complexity and abstraction there.
- empath75 8y agorust doesn't use dynamic linking, which contributes a lot to that size.
- steveklabnik 8y agoIt can, but for practical reasons, generally does not. For Rust code anyway; often anything bound via FFI is dynamically linked.
- kingosticks 8y agoWont a crate compiled with 'crate_type = "dylib"' be dynamically linked if you specify '-C prefer-dynamic' when compiling your program? https://doc.rust-lang.org/reference/linkage.html https://doc.rust-lang.org/reference/linkage.html
- chewbacha 8y agoI recently ported a very small micro service from Rocket to Actix and found the migration to be painless. In fact, it's use of types and inferencing along with integration with Serde made it very easy. It also worked on stable-rust and can work with connection pooling against postgres. This makes it a winner in my mind. I'm excited to use it again in my next service.
- rubyfan 8y agoWhy do so many Rust projects lead with telling you their number one feature is type safety? It’s Rust we get it, stop telling us about your type safety! Also, what problem is this solving that countless other near identical web frameworks don’t already solve?
- smt88 8y agoEven with an opinionated type system, it's possible to slack off a bit. There are different degrees of strictness/specificity when composing types.
- pjmlp 8y agoA few devs are over enthusiast and missed the 80/90's wave of safe systems languages.
- strkek 8y agoMost projects use buzzwords like that though. - Safe! - Blazing fast! - Powerful! - Elegant! - Shiny! - Modern! You know, marketing stuff that tells you absolutely nothing about the software itself but is required to fill space on a website. The thing is, a boring "Benchmarks" heading in the README doesn't get you as many GitHub stars as a "Blazingly fast!" heading+icon. And you know how addictive Internet Points can be.
- sudeepj 8y ago> fn greet(req: HttpRequest) -> impl Responder Nice to see front page example using 'impl', the recent most improvement in ergonomics. Before 1.26 things would be different. This makes me more appreciative of the efforts from Rust team/community to improve the ease of use.
- rkangel 8y agoTo explain this to people who don't know the background - this is about 'impl NameOfTrait' in the return position. It allows functions to return an object that provides a certain interface without specifying the actual type of that object. This was only previously true by wrapping it in a 'box', which meant a heap allocation and dynamic dispatch. The 'impl trait' provides static dispatch and no other overhead, so produces equivalent code to returning the type directly, but with all the abstraction flexibility that you want.
- ejanus 8y agoWhat is the use case ? Is there simple examples one could go through?
- iopq 8y agoUsually you return a closure with impl Fn. Another use case is to return an iterator. I wrote a post about it. Let me know if it helps. https://medium.com/@iopguy/impl-trait-in-rust-explanation-efde0d94946a https://medium.com/@iopguy/impl-trait-in-rust-explanation-ef...
- jason_oster 8y agoI evaluated a few different Rust web frameworks, where performance was the deciding factor. A minimum viable echo server was put through its paces with `h2load` on a relatively recent MacBook Pro. `actix-web` was literally 100x faster than the next fastest competing framework. The benchmark result really confused me. But you'll find that the `actix` actors are extraordinarily lightweight and highly optimized around the equally lightweight and highly optimized Futures. The design is hard to beat, from a performance standpoint.
- madsohm 8y agoHave you tried to create a more complex web page and have the web frameworks render that as well? Sometimes being the fastest at the most trivial request isn't enough, if it can't handle complex request fast as well. Also, do you have a list of speeds for the frameworks you've tested?
- steveklabnik 8y agoA lot of Rust frameworks use sync io. The first generation does because the libraries for async didn't exist yet, and Rocket doesn't because (last I heard), the author said that he didn't feel the ergonomics were there yet, and that's one of Rocket's primary goals. So that leaves Actix, Gotham, and Shio, basically. Gotham hasn't been tuned for performance at all. I haven't seen any Shio benchmarks. There are a lot though: https://github.com/flosse/rust-web-framework-comparison#server-frameworks https://github.com/flosse/rust-web-framework-comparison#serv...
- fafhrd91 8y agoFrom repo activity Shio seems dead. And here is for gotham https://gotham.rs/blog/2018/05/31/the-state-of-gotham.html https://gotham.rs/blog/2018/05/31/the-state-of-gotham.html
- steveklabnik 8y agoYeah, that Gotham news happened after I made this comment. Good to know!
- baituhuangyu 8y agoreally?
- baituhuangyu 8y agoreally?
- some_account 8y agoI really love seeing rust making progress. It's a fantastic (with slightly ugly syntax) language that I want to learn. :)
- kev009 8y agoThis looks really nice! I learned Scala primarily to use the Play Framework which is a fabulous way to build large web applications. This looks spiritually quite similar, but with the advantages of Rust.
- lewisinc 8y agoFunny, I'm learning Scala for work after teaching myself Rust. It's definitely made the whole process a lot more straightforward thanks to the commonalities between them.
- user1241320 8y agoI started using Scala for Play and Akka and I'm currently using Akka[1] in production in many projects. [1]: http://akka.io/ http://akka.io/ Akka is the implementation of the Actor Model on the JVM.
- znfi 8y agoNot sure if I'm missing something, but for the Techempower Bencharks [1] I had the impression that the bottleneck for other rust libraries were in accessing the database rather than handling http requests. However, looking at the code [2] it seems that the Actix solution isn't doing anything special with regards to this. Can someone give a quick description of "what" is causing such a huge performance boost for Actix compared to other frameworks? (I might add that this is a question I've had for a while, and I did not check the source at [2] in detail today.) [1]: https://www.techempower.com/benchmarks/ https://www.techempower.com/benchmarks/ [2]: https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Rust/actix https://github.com/TechEmpower/FrameworkBenchmarks/tree/mast...
- steveklabnik 8y agoThese kinds of tests are heavily reliant on having async IO, and https://news.ycombinator.com/item?id=17194761 https://news.ycombinator.com/item?id=17194761
- znfi 8y agoAny chance you could elaborate on this, because I dont really understand how it answers my original question. I have not checked recently, but last I saw, the database libraries for rust did not use async IO. Looking at (what I presume is) the code for the benchmark [1], it seems it imports the postgres and diesel crates. Last I heard diesel did not support async [2] and looking at the postgres crate [3] it does not mention async, which I assume it would in case it was supported. My whole point was that, sure, I can see how async IO is important for handling many concurrent http requests, but each of those requests would still have to pass through the synchronous database driver which uses threadpooling, right? Or what am I missing here? I can see how it has great performance on the plaintext and json benchamrks, but I dont understand what gives it such a large boost in fortunes or multiple queries. For example, Iron is doing 300k at plaintext/json benchmarks, but drops to 18k on fortunes, and the way I remember the benchmark code it is written in a fairly straight forward way. If the database layer supported 160k requests per second I dont see why we would see such a huge drop? (Edit: 160k is the performance of Actix on fortunes.) I also recall seeing numbers on the 10k order of magnitude from doing naive benchmarks with the various database libraries available, without any http part to the application. But I'm not sure, maybe I'm missing something or remember incorrectly? [1]: https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/frameworks/Rust/actix/src/main_pg.rs https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast... [2]: https://github.com/diesel-rs/diesel/issues/399 https://github.com/diesel-rs/diesel/issues/399 [3]: https://crates.io/crates/postgres https://crates.io/crates/postgres
- ccccccccccccc 8y agoQuestion: What is the point of making the fastest web server possible when any kind of datastore attached to the server is going to be the bottleneck?
- steveklabnik 8y agoThat's only true for certain workloads and applications.
- tene 8y agoThat's a good question, and something that's not always obvious without having been in a situation to get advantage from such an improvement. First, even if waiting for a response from the database is the largest single contributor to your response time, and if you're running an extremely low-traffic service, there's still benefit in reducing your total latency Second, if you're not running a low-traffic service, and have enough requests that you're approaching the memory or cpu capacity of your web server (or have an existing application that's already deployed across multiple servers), making significant reduction in your CPU or memory use can let you handle quite a bit more traffic with less hardware. Third, not all web services involve little more than making a request to a single external slow database. The data for your service could be: * Static * Ephemeral, kept in-process * In a database on the same server * In a fast database (memcache) * Require nontrivial processing * Processed by a separate service you're just acting as a proxy for
- Jweb_Guru 8y agoMany modern databases are very fast and many modern web servers are very slow. I've run into many applications where database performance was not the bottleneck.