remove legacy, update README
229
README.md
|
@ -16,25 +16,21 @@ web application framework](https://facil.io).
|
||||||
## **⚡ZAP⚡ IS FAST, ROBUST, AND STABLE**
|
## **⚡ZAP⚡ IS FAST, ROBUST, AND STABLE**
|
||||||
|
|
||||||
|
|
||||||
After having used ZAP in production for a year, I can confidently assert that it
|
After having used ZAP in production for years, I can confidently assert that it
|
||||||
proved to be:
|
proved to be:
|
||||||
|
|
||||||
- ⚡ **blazingly fast** ⚡
|
- ⚡ **blazingly fast** ⚡
|
||||||
- 💪 **extremely robust** 💪
|
- 💪 **extremely robust** 💪
|
||||||
|
|
||||||
Exactly the goals I set out to achieve!
|
|
||||||
|
|
||||||
## FAQ:
|
## FAQ:
|
||||||
|
|
||||||
- Q: **What version of Zig does Zap support?**
|
- Q: **What version of Zig does Zap support?**
|
||||||
- Zap uses the latest stable zig release (0.13.0), so you don't have to keep
|
- Zap uses the latest stable zig release (0.14.0), so you don't have to keep
|
||||||
up with frequent breaking changes. It's an "LTS feature".
|
up with frequent breaking changes. It's an "LTS feature".
|
||||||
- Q: **Can Zap build with Zig's master branch?**
|
- Q: **Can Zap build with Zig's master branch?**
|
||||||
- See the `zig-master` branch. An example of how to use it is
|
- See the `zig-master` branch. Please note that the zig-master branch is not
|
||||||
[here](https://github.com/zigzap/hello-master). Please note that the
|
the official master branch of ZAP. Be aware that I don't provide tagged
|
||||||
zig-master branch is not the official master branch of ZAP. Be aware that
|
releases for it. If you know what you are doing, that shouldn't stop you
|
||||||
I don't provide `build.zig.zon` snippets or tagged releases for it for
|
|
||||||
the time being. If you know what you are doing, that shouldn't stop you
|
|
||||||
from using it with zig master though.
|
from using it with zig master though.
|
||||||
- Q: **Where is the API documentation?**
|
- Q: **Where is the API documentation?**
|
||||||
- Docs are a work in progress. You can check them out
|
- Docs are a work in progress. You can check them out
|
||||||
|
@ -43,7 +39,7 @@ Exactly the goals I set out to achieve!
|
||||||
- Q: **Does ZAP work on Windows?**
|
- Q: **Does ZAP work on Windows?**
|
||||||
- No. This is due to the underlying facil.io C library. Future versions
|
- No. This is due to the underlying facil.io C library. Future versions
|
||||||
of facil.io might support Windows but there is no timeline yet. Your best
|
of facil.io might support Windows but there is no timeline yet. Your best
|
||||||
options on Windows are WSL2 or a docker container.
|
options on Windows are **WSL2 or a docker container**.
|
||||||
- Q: **Does ZAP support TLS / HTTPS?**
|
- Q: **Does ZAP support TLS / HTTPS?**
|
||||||
- Yes, ZAP supports using the system's openssl. See the
|
- Yes, ZAP supports using the system's openssl. See the
|
||||||
[https](./examples/https/https.zig) example and make sure to build with
|
[https](./examples/https/https.zig) example and make sure to build with
|
||||||
|
@ -55,29 +51,43 @@ Exactly the goals I set out to achieve!
|
||||||
|
|
||||||
## Here's what works
|
## Here's what works
|
||||||
|
|
||||||
I recommend checking out **Endpoint-based examples for more realistic
|
**NOTE:** I recommend checking out **the new App-based** or the Endpoint-based
|
||||||
use cases**. Most of the examples are super stripped down to only include
|
examples, as they reflect how I intended Zap to be used.
|
||||||
what's necessary to show a feature.
|
|
||||||
|
|
||||||
**NOTE: To see API docs, run `zig build run-docserver`.** To specify a custom
|
Most of the examples are super stripped down to only include what's necessary to
|
||||||
|
show a feature.
|
||||||
|
|
||||||
|
**To see API docs, run `zig build run-docserver`.** To specify a custom
|
||||||
port and docs dir: `zig build docserver && zig-out/bin/docserver --port=8989
|
port and docs dir: `zig build docserver && zig-out/bin/docserver --port=8989
|
||||||
--docs=path/to/docs`.
|
--docs=path/to/docs`.
|
||||||
|
|
||||||
- **Super easy build process**: Zap's `build.zig` now uses the new Zig package
|
### New App-Based Examples
|
||||||
manager for its C-dependencies, no git submodules anymore.
|
|
||||||
- _tested on Linux and macOS (arm, M1)_
|
- **[app_basic](examples/app/basic.zig)**: Shows how to use zap.App with a
|
||||||
- **[hello](examples/hello/hello.zig)**: welcomes you with some static HTML
|
simple Endpoint.
|
||||||
- **[routes](examples/routes/routes.zig)**: a super easy example dispatching on
|
- **[app_basic](examples/app/auth.zig)**: Shows how to use zap.App with an
|
||||||
the HTTP path. **NOTE**: The dispatch in the example is a super-basic
|
Endpoint using an Authenticator.
|
||||||
DIY-style dispatch. See endpoint-based examples for more realistic use cases.
|
|
||||||
- **[serve](examples/serve/serve.zig)**: the traditional static web server with
|
See the other examples for specific uses of Zap.
|
||||||
optional dynamic request handling
|
|
||||||
- **[sendfile](examples/sendfile/sendfile.zig)**: simple example of how to send
|
Benefits of using `zap.App`:
|
||||||
a file, honoring compression headers, etc.
|
|
||||||
- **[bindataformpost](examples/bindataformpost/bindataformpost.zig)**: example
|
- Provides a global, user-defined "Application Context" to all endpoints.
|
||||||
to receive binary files via form post.
|
- Made to work with "Endpoints": an endpoint is a struct that covers a `/slug`
|
||||||
- **[hello_json](examples/hello_json/hello_json.zig)**: serves you json
|
of the requested URL and provides a callback for each supported request method
|
||||||
dependent on HTTP path
|
(get, put, delete, options, post, head, patch).
|
||||||
|
- Each request callback receives:
|
||||||
|
- a per-thread arena allocator you can use for throwaway allocations without
|
||||||
|
worrying about freeing them.
|
||||||
|
- the global "Application Context" of your app's choice
|
||||||
|
- Endpoint request callbacks are allowed to return errors:
|
||||||
|
- you can use `try`.
|
||||||
|
- the endpoint's ErrorStrategy defines if runtime errors should be reported to
|
||||||
|
the console, to the response (=browser for debugging), or if the error
|
||||||
|
should be returned.
|
||||||
|
|
||||||
|
### Legacy Endpoint-based examples
|
||||||
|
|
||||||
- **[endpoint](examples/endpoint/)**: a simple JSON REST API example featuring a
|
- **[endpoint](examples/endpoint/)**: a simple JSON REST API example featuring a
|
||||||
`/users` endpoint for performing PUT/DELETE/GET/POST operations and listing
|
`/users` endpoint for performing PUT/DELETE/GET/POST operations and listing
|
||||||
users, together with a simple frontend to play with. **It also introduces a
|
users, together with a simple frontend to play with. **It also introduces a
|
||||||
|
@ -87,26 +97,16 @@ port and docs dir: `zig build docserver && zig-out/bin/docserver --port=8989
|
||||||
`GeneralPurposeAllocator` to report memory leaks when ZAP is shut down.
|
`GeneralPurposeAllocator` to report memory leaks when ZAP is shut down.
|
||||||
The [StopEndpoint](examples/endpoint/stopendpoint.zig) just stops ZAP when
|
The [StopEndpoint](examples/endpoint/stopendpoint.zig) just stops ZAP when
|
||||||
receiving a request on the `/stop` route.
|
receiving a request on the `/stop` route.
|
||||||
- **[mustache](examples/mustache/mustache.zig)**: a simple example using
|
|
||||||
[mustache](https://mustache.github.io/) templating.
|
|
||||||
- **[endpoint authentication](examples/endpoint_auth/endpoint_auth.zig)**: a
|
- **[endpoint authentication](examples/endpoint_auth/endpoint_auth.zig)**: a
|
||||||
simple authenticated endpoint. Read more about authentication
|
simple authenticated endpoint. Read more about authentication
|
||||||
[here](./doc/authentication.md).
|
[here](./doc/authentication.md).
|
||||||
- **[http parameters](examples/http_params/http_params.zig)**: a simple example
|
|
||||||
sending itself query parameters of all supported types.
|
|
||||||
- **[cookies](examples/cookies/cookies.zig)**: a simple example sending itself a
|
### Legacy Middleware-Style examples
|
||||||
cookie and responding with a session cookie.
|
|
||||||
- **[websockets](examples/websockets/)**: a simple websockets chat for the
|
|
||||||
browser.
|
|
||||||
- **[Username/Password Session
|
|
||||||
Authentication](./examples/userpass_session_auth/)**: A convenience
|
|
||||||
authenticator that redirects un-authenticated requests to a login page and
|
|
||||||
sends cookies containing session tokens based on username/password pairs
|
|
||||||
received via POST request.
|
|
||||||
- **[MIDDLEWARE support](examples/middleware/middleware.zig)**: chain together
|
- **[MIDDLEWARE support](examples/middleware/middleware.zig)**: chain together
|
||||||
request handlers in middleware style. Provide custom context structs, totally
|
request handlers in middleware style. Provide custom context structs, totally
|
||||||
type-safe, using **[ZIG-CEPTION](doc/zig-ception.md)**. If you come from GO
|
type-safe. If you come from GO this might appeal to you.
|
||||||
this might appeal to you.
|
|
||||||
- **[MIDDLEWARE with endpoint
|
- **[MIDDLEWARE with endpoint
|
||||||
support](examples/middleware_with_endpoint/middleware_with_endpoint.zig)**:
|
support](examples/middleware_with_endpoint/middleware_with_endpoint.zig)**:
|
||||||
Same as the example above, but this time we use an endpoint at the end of the
|
Same as the example above, but this time we use an endpoint at the end of the
|
||||||
|
@ -126,6 +126,36 @@ port and docs dir: `zig build docserver && zig-out/bin/docserver --port=8989
|
||||||
struct in the callbacks via the `@fieldParentPtr()` trick that is used
|
struct in the callbacks via the `@fieldParentPtr()` trick that is used
|
||||||
extensively in Zap's examples, like the [endpoint
|
extensively in Zap's examples, like the [endpoint
|
||||||
example](examples/endpoint/endpoint.zig).
|
example](examples/endpoint/endpoint.zig).
|
||||||
|
|
||||||
|
### Specific and Very Basic Examples
|
||||||
|
|
||||||
|
- **[hello](examples/hello/hello.zig)**: welcomes you with some static HTML
|
||||||
|
- **[routes](examples/routes/routes.zig)**: a super easy example dispatching on
|
||||||
|
the HTTP path. **NOTE**: The dispatch in the example is a super-basic
|
||||||
|
DIY-style dispatch. See endpoint-based examples for more realistic use cases.
|
||||||
|
- [**simple_router**](examples/simple_router/simple_router.zig): See how you
|
||||||
|
can use `zap.Router` to dispatch to handlers by HTTP path.
|
||||||
|
- **[serve](examples/serve/serve.zig)**: the traditional static web server with
|
||||||
|
optional dynamic request handling
|
||||||
|
- **[sendfile](examples/sendfile/sendfile.zig)**: simple example of how to send
|
||||||
|
a file, honoring compression headers, etc.
|
||||||
|
- **[bindataformpost](examples/bindataformpost/bindataformpost.zig)**: example
|
||||||
|
to receive binary files via form post.
|
||||||
|
- **[hello_json](examples/hello_json/hello_json.zig)**: serves you json
|
||||||
|
dependent on HTTP path
|
||||||
|
- **[mustache](examples/mustache/mustache.zig)**: a simple example using
|
||||||
|
[mustache](https://mustache.github.io/) templating.
|
||||||
|
- **[http parameters](examples/http_params/http_params.zig)**: a simple example
|
||||||
|
sending itself query parameters of all supported types.
|
||||||
|
- **[cookies](examples/cookies/cookies.zig)**: a simple example sending itself a
|
||||||
|
cookie and responding with a session cookie.
|
||||||
|
- **[websockets](examples/websockets/)**: a simple websockets chat for the
|
||||||
|
browser.
|
||||||
|
- **[Username/Password Session
|
||||||
|
Authentication](./examples/userpass_session_auth/)**: A convenience
|
||||||
|
authenticator that redirects un-authenticated requests to a login page and
|
||||||
|
sends cookies containing session tokens based on username/password pairs
|
||||||
|
received via POST request.
|
||||||
- [**Error Trace Responses**](./examples/senderror/senderror.zig): You can now
|
- [**Error Trace Responses**](./examples/senderror/senderror.zig): You can now
|
||||||
call `r.sendError(err, status_code)` when you catch an error and a stack trace
|
call `r.sendError(err, status_code)` when you catch an error and a stack trace
|
||||||
will be returned to the client / browser.
|
will be returned to the client / browser.
|
||||||
|
@ -136,12 +166,6 @@ port and docs dir: `zig build docserver && zig-out/bin/docserver --port=8989
|
||||||
- run it like this: `ZAP_USE_OPENSSL=true zig build run-https`
|
- run it like this: `ZAP_USE_OPENSSL=true zig build run-https`
|
||||||
OR like this: `zig build -Dopenssl=true run-https`
|
OR like this: `zig build -Dopenssl=true run-https`
|
||||||
- it will tell you how to generate certificates
|
- it will tell you how to generate certificates
|
||||||
- [**simple_router**](examples/simple_router/simple_router.zig): See how you
|
|
||||||
can use `zap.Router` to dispatch to handlers by HTTP path.
|
|
||||||
|
|
||||||
I'll continue wrapping more of facil.io's functionality and adding stuff to zap
|
|
||||||
to a point where I can use it as the JSON REST API backend for real research
|
|
||||||
projects, serving thousands of concurrent clients.
|
|
||||||
|
|
||||||
|
|
||||||
## ⚡blazingly fast⚡
|
## ⚡blazingly fast⚡
|
||||||
|
@ -158,25 +182,7 @@ machine (x86_64-linux):
|
||||||
|
|
||||||
- Zig Zap was nearly 30% faster than GO
|
- Zig Zap was nearly 30% faster than GO
|
||||||
- Zig Zap had over 50% more throughput than GO
|
- Zig Zap had over 50% more throughput than GO
|
||||||
|
- **YMMV !!!**
|
||||||
**Update**: Thanks to @felipetrz, I got to test against more realistic Python
|
|
||||||
and Rust examples. Both python `sanic` and rust `axum` were easy enough to
|
|
||||||
integrate.
|
|
||||||
|
|
||||||
**Update**: I have automated the benchmarks. See
|
|
||||||
[blazingly-fast.md](./blazingly-fast.md) for more information. Also, thanks to
|
|
||||||
@alexpyattaev, the benchmarks are fairer now, pinning server and client to
|
|
||||||
specific CPU cores.
|
|
||||||
|
|
||||||
**Update**: I have consolidated the benchmarks to one good representative per
|
|
||||||
language. See more details in [blazingly-fast.md](./blazingly-fast.md). It
|
|
||||||
contains rust implementations that come pretty close to Zap's performance in the
|
|
||||||
simplistic testing scenario.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
So, being somewhere in the ballpark of basic GO performance, zig zap seems to be
|
So, being somewhere in the ballpark of basic GO performance, zig zap seems to be
|
||||||
... of reasonable performance 😎.
|
... of reasonable performance 😎.
|
||||||
|
@ -184,7 +190,38 @@ So, being somewhere in the ballpark of basic GO performance, zig zap seems to be
|
||||||
I can rest my case that developing ZAP was a good idea because it's faster than
|
I can rest my case that developing ZAP was a good idea because it's faster than
|
||||||
both alternatives: a) staying with Python, and b) creating a GO + Zig hybrid.
|
both alternatives: a) staying with Python, and b) creating a GO + Zig hybrid.
|
||||||
|
|
||||||
See more details in [blazingly-fast.md](blazingly-fast.md).
|
### On (now missing) Micro-Benchmakrs
|
||||||
|
|
||||||
|
I used to have some micro-benchmarks in this repo, showing that Zap beat all the
|
||||||
|
other things I tried, and eventually got tired of the meaningless discussions
|
||||||
|
they provoked, the endless issues and PRs that followed, wanting me to add and
|
||||||
|
maintain even more contestants, do more justice to beloved other frameworks,
|
||||||
|
etc.
|
||||||
|
|
||||||
|
Case in point, even for me the micro-benchmarks became meaningless. They were
|
||||||
|
just some rough indicator to me confirming that I didn't do anything terribly
|
||||||
|
wrong to facil.io, and that facil.io proved to be a reasonable choice, also from
|
||||||
|
a performance perspective.
|
||||||
|
|
||||||
|
However, none of the projects I use Zap for, ever even remotely resembled
|
||||||
|
anything close to a static HTTP response micro-benchmark.
|
||||||
|
|
||||||
|
For my more CPU-heavy than IO-heavy use-cases, a thread-based microframework
|
||||||
|
that's super robust is still my preferred choice, to this day.
|
||||||
|
|
||||||
|
Having said that, I would **still love** for other, pure-zig HTTP frameworks to
|
||||||
|
eventually make Zap obsolete. Now, in 2025, the list of candidates is looking
|
||||||
|
really promising.
|
||||||
|
|
||||||
|
### 📣 Shout-Outs
|
||||||
|
|
||||||
|
- [httpz](https://github.com/karlseguin/http.zig) : Pure Zig! Closer to Zap's
|
||||||
|
model. Performance = good!
|
||||||
|
- [jetzig](https://github.com/jetzig-framework/jetzig) : Comfortably develop
|
||||||
|
modern web applications quickly, using http.zig under the hood
|
||||||
|
- [zzz](https://github.com/tardy-org/zzz) : Super promising, super-fast,
|
||||||
|
especially for IO-heavy tasks, io_uring support - need I say more?
|
||||||
|
|
||||||
|
|
||||||
## 💪 Robust
|
## 💪 Robust
|
||||||
|
|
||||||
|
@ -217,9 +254,10 @@ local variables that require tens of megabytes of stack space.
|
||||||
### 🛡️ Memory-safe
|
### 🛡️ Memory-safe
|
||||||
|
|
||||||
See the [StopEndpoint](examples/endpoint/stopendpoint.zig) in the
|
See the [StopEndpoint](examples/endpoint/stopendpoint.zig) in the
|
||||||
[endpoint](examples/endpoint) example. That example uses ZIG's awesome
|
[endpoint](examples/endpoint) example. The `StopEndpoint` just stops ZAP when
|
||||||
`GeneralPurposeAllocator` to report memory leaks when ZAP is shut down. The
|
receiving a request on the `/stop` route. That example uses ZIG's awesome
|
||||||
`StopEndpoint` just stops ZAP when receiving a request on the `/stop` route.
|
`GeneralPurposeAllocator` in [main.zig](examples/endpoint/main.zig) to report
|
||||||
|
memory leaks when ZAP is shut down.
|
||||||
|
|
||||||
You can use the same strategy in your debug builds and tests to check if your
|
You can use the same strategy in your debug builds and tests to check if your
|
||||||
code leaks memory.
|
code leaks memory.
|
||||||
|
@ -228,7 +266,7 @@ code leaks memory.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Make sure you have **zig 0.13.0** installed. Fetch it from
|
Make sure you have **zig 0.14.0** installed. Fetch it from
|
||||||
[here](https://ziglang.org/download).
|
[here](https://ziglang.org/download).
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -237,12 +275,11 @@ $ cd zap
|
||||||
$ zig build run-hello
|
$ zig build run-hello
|
||||||
$ # open http://localhost:3000 in your browser
|
$ # open http://localhost:3000 in your browser
|
||||||
```
|
```
|
||||||
|
|
||||||
... and open [http://localhost:3000](http://localhost:3000) in your browser.
|
... and open [http://localhost:3000](http://localhost:3000) in your browser.
|
||||||
|
|
||||||
## Using ⚡zap⚡ in your own projects
|
## Using ⚡zap⚡ in your own projects
|
||||||
|
|
||||||
Make sure you have **the latest zig release (0.13.0)** installed. Fetch it from
|
Make sure you have **the latest zig release (0.14.0)** installed. Fetch it from
|
||||||
[here](https://ziglang.org/download).
|
[here](https://ziglang.org/download).
|
||||||
|
|
||||||
If you don't have an existing zig project, create one like this:
|
If you don't have an existing zig project, create one like this:
|
||||||
|
@ -250,17 +287,11 @@ If you don't have an existing zig project, create one like this:
|
||||||
```shell
|
```shell
|
||||||
$ mkdir zaptest && cd zaptest
|
$ mkdir zaptest && cd zaptest
|
||||||
$ zig init
|
$ zig init
|
||||||
$ git init ## (optional)
|
|
||||||
```
|
```
|
||||||
**Note**: Nix/NixOS users are lucky; you can use the existing `flake.nix` and run
|
|
||||||
`nix develop` to get a development shell providing zig and all
|
|
||||||
dependencies to build and run the GO, python, and rust examples for the
|
|
||||||
`wrk` performance tests. For the mere building of zap projects,
|
|
||||||
`nix develop .#build` will only fetch zig 0.11.0. TODO: upgrade to latest zig.
|
|
||||||
|
|
||||||
With an existing Zig project, adding Zap to it is easy:
|
With an existing Zig project, adding Zap to it is easy:
|
||||||
|
|
||||||
1. Add zap to your `build.zig.zon`
|
1. Zig fetch zap
|
||||||
2. Add zap to your `build.zig`
|
2. Add zap to your `build.zig`
|
||||||
|
|
||||||
In your zig project folder (where `build.zig` is located), run:
|
In your zig project folder (where `build.zig` is located), run:
|
||||||
|
@ -284,26 +315,9 @@ Then, in your `build.zig`'s `build` function, add the following before
|
||||||
exe.root_module.addImport("zap", zap.module("zap"));
|
exe.root_module.addImport("zap", zap.module("zap"));
|
||||||
```
|
```
|
||||||
|
|
||||||
From then on, you can use the Zap package in your project. Check out the
|
From then on, you can use the Zap package in your project via `const zap =
|
||||||
examples to see how to use Zap.
|
@import("zap");`. Check out the examples to see how to use Zap.
|
||||||
|
|
||||||
## Updating your project to the latest version of zap
|
|
||||||
|
|
||||||
You can change the URL to Zap in your `build.zig.zon`
|
|
||||||
|
|
||||||
- easiest: use a tagged release
|
|
||||||
- or to one of the tagged versions, e.g. `0.0.9`
|
|
||||||
- or to the latest commit of `zap`
|
|
||||||
|
|
||||||
### Using a tagged release
|
|
||||||
|
|
||||||
Go to the [release page](https://github.com/zigzap/zap/releases). Every release
|
|
||||||
will state its version number and also provide instructions for changing
|
|
||||||
`build.zig.zon` and `build.zig`.
|
|
||||||
|
|
||||||
### Using other versions
|
|
||||||
|
|
||||||
See [here](./doc/other-versions.md).
|
|
||||||
|
|
||||||
## Contribute to ⚡zap⚡ - blazingly fast
|
## Contribute to ⚡zap⚡ - blazingly fast
|
||||||
|
|
||||||
|
@ -314,16 +328,7 @@ world a blazingly fast place by providing patches or pull requests, add
|
||||||
documentation or examples, or interesting issues and bug reports - you'll know
|
documentation or examples, or interesting issues and bug reports - you'll know
|
||||||
what to do when you receive your calling 👼.
|
what to do when you receive your calling 👼.
|
||||||
|
|
||||||
Check out [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
|
**We have our own [ZAP discord](https://discord.gg/jQAAN6Ubyj) server!!!**
|
||||||
|
|
||||||
See also [introducing.md](introducing.md) for more on the state and progress of
|
|
||||||
this project.
|
|
||||||
|
|
||||||
**We now have our own [ZAP discord](https://discord.gg/jQAAN6Ubyj) server!!!**
|
|
||||||
|
|
||||||
You can also reach me on [the zig showtime discord
|
|
||||||
server](https://discord.gg/CBzE3VMb) under the handle renerocksai
|
|
||||||
(renerocksai#1894).
|
|
||||||
|
|
||||||
## Support ⚡zap⚡
|
## Support ⚡zap⚡
|
||||||
|
|
||||||
|
|
|
@ -1,332 +0,0 @@
|
||||||
# ⚡blazingly fast⚡
|
|
||||||
|
|
||||||
Initially, I conducted a series of quick tests, using wrk with simple HTTP
|
|
||||||
servers written in GO and in zig zap. I made sure that all servers only output
|
|
||||||
17 bytes of HTTP body.
|
|
||||||
|
|
||||||
Just to get some sort of indication, I also included measurements for python
|
|
||||||
since I used to write my REST APIs in python before creating zig zap.
|
|
||||||
|
|
||||||
You can check out the scripts I used for the tests in [./wrk](wrk/).
|
|
||||||
|
|
||||||
## Why
|
|
||||||
|
|
||||||
I aimed to enhance the performance of my Python + Flask backends by replacing
|
|
||||||
them with a Zig version. To evaluate the success of this transition, I compared
|
|
||||||
the performance of a static HTTP server implemented in Python and its Zig
|
|
||||||
counterpart, which showed significant improvements.
|
|
||||||
|
|
||||||
To further assess the Zig server's performance, I compared it with a Go
|
|
||||||
implementation, to compare against a widely used industry-standard. I expected
|
|
||||||
similar performance levels but was pleasantly surprised when Zap outperformed Go
|
|
||||||
by approximately 30% on my test machine.
|
|
||||||
|
|
||||||
Intrigued by Rust's reputed performance capabilities, I also experimented with a
|
|
||||||
Rust version. The results of this experiment are discussed in the
|
|
||||||
[Flaws](#flaws) section below.
|
|
||||||
|
|
||||||
## What
|
|
||||||
|
|
||||||
So, what are the benchmarks testing?
|
|
||||||
|
|
||||||
- simple http servers that reply to GET requests with a constant, 17-bytes long response
|
|
||||||
- 4 cores are assigned to the subject under test (the respective server)
|
|
||||||
- 4 cores are assigned to `wrk`
|
|
||||||
- using 4 threads
|
|
||||||
- aiming at 400 concurrent connections
|
|
||||||
|
|
||||||
## How
|
|
||||||
|
|
||||||
I have fully automated the benchmarks and graph generation.
|
|
||||||
|
|
||||||
To generate the data:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ ./wrk/measure_all.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
To generate the graphs:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ python wrk/graph.py
|
|
||||||
```
|
|
||||||
|
|
||||||
For dependencies, please see the [flake.nix](./flake.nix#L46).
|
|
||||||
|
|
||||||
## Flaws
|
|
||||||
|
|
||||||
The benchmarks have limitations, such as the lack of request latencies. The Rust
|
|
||||||
community has often criticized these benchmarks as biased. However, no such
|
|
||||||
criticisms have come from the Go or Python communities.
|
|
||||||
|
|
||||||
In response to the Rust community's concerns, we've added three Rust
|
|
||||||
implementations for comparison:
|
|
||||||
|
|
||||||
- A standard version from [the Rust book](https://doc.rust-lang.org/book/ch20-00-final-project-a-web-server.html).
|
|
||||||
- An "axum" version to highlight Rust's speed.
|
|
||||||
- A refined version of the Rust book version.
|
|
||||||
|
|
||||||
Originally, the goal was to compare "batteries included" versions, which created
|
|
||||||
a disparity by comparing the optimized zap / facil.io code with basic bundled
|
|
||||||
functionalities. These tests were for personal interest and not meant to be
|
|
||||||
definitive benchmarks.
|
|
||||||
|
|
||||||
To address this bias, we've added the Rust-axum and Python-sanic benchmarks. For
|
|
||||||
more information, refer to the relevant discussions and pull requests.
|
|
||||||
|
|
||||||
|
|
||||||
## More benchmarks?
|
|
||||||
|
|
||||||
I often receive requests or PRs to include additional benchmarks, which a lot of
|
|
||||||
times I find to be either ego-driven or a cause for unnecessary disputes. People
|
|
||||||
tend to favor their preferred language or framework. Zig, Rust, C, and C++ are
|
|
||||||
all capable of efficiently creating fast web servers, with different frameworks
|
|
||||||
potentially excelling in certain benchmarks. My main concern was whether Zap,
|
|
||||||
given its current level of abstraction, could compete with standard web servers.
|
|
||||||
This question has been answered, and I see no need for further benchmarks.
|
|
||||||
|
|
||||||
So far, we have the following benchmark subjects (implementations) which you'll
|
|
||||||
find in the graphs below:
|
|
||||||
|
|
||||||
- **zig-zap** : ZAP implementation
|
|
||||||
- **go** : GO implementation
|
|
||||||
- **python** : Python implementation
|
|
||||||
- **python-sanic** : Python implementation with sanic framework
|
|
||||||
- **rust-bythebook** : Rust example from the Rust book (not representative)
|
|
||||||
- **rust-bythebook-improved** : Improved version of the by-the-book code (thx @alexpyattaev)
|
|
||||||
- **rust-clean** : A clean, straight-forward Rust implementation (thx @alexpyattaev)
|
|
||||||
- **rust-axum** : Rust implementation using the axum framework (realistic)
|
|
||||||
- **(csharp)** : CSharp implementation (thx @leo-costa)
|
|
||||||
- **cpp-beast** : A C++ implementation using boost::beast (thx @kassane)
|
|
||||||
|
|
||||||
|
|
||||||
## The computer makes the difference
|
|
||||||
|
|
||||||
After automating the performance benchmarks, I gathered data from three
|
|
||||||
different computers. It's interesting to see the variation in relative numbers.
|
|
||||||
|
|
||||||
|
|
||||||
### The test machine (graphs in the README)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
```
|
|
||||||
➜ neofetch --stdout
|
|
||||||
rs@ryzen
|
|
||||||
--------
|
|
||||||
OS: NixOS 23.05.997.ddf4688dc7a (Stoat) x86_64
|
|
||||||
Host: Micro-Star International Co., Ltd. B550-A PRO (MS-7C56)
|
|
||||||
Kernel: 6.3.7
|
|
||||||
Uptime: 15 days, 11 hours, 13 mins
|
|
||||||
Packages: 2094 (nix-system), 1356 (nix-user), 7 (flatpak)
|
|
||||||
Shell: bash 5.2.15
|
|
||||||
Resolution: 3840x2160
|
|
||||||
DE: none+i3
|
|
||||||
WM: i3
|
|
||||||
Terminal: tmux
|
|
||||||
CPU: AMD Ryzen 5 5600X (12) @ 3.700GHz
|
|
||||||
GPU: AMD ATI Radeon RX 6700/6700 XT/6750 XT / 6800M/6850M XT
|
|
||||||
Memory: 4981MiB / 32028MiB
|
|
||||||
|
|
||||||
|
|
||||||
➜ lscpu
|
|
||||||
Architecture: x86_64
|
|
||||||
CPU op-mode(s): 32-bit, 64-bit
|
|
||||||
Address sizes: 48 bits physical, 48 bits virtual
|
|
||||||
Byte Order: Little Endian
|
|
||||||
CPU(s): 12
|
|
||||||
On-line CPU(s) list: 0-11
|
|
||||||
Vendor ID: AuthenticAMD
|
|
||||||
Model name: AMD Ryzen 5 5600X 6-Core Processor
|
|
||||||
CPU family: 25
|
|
||||||
Model: 33
|
|
||||||
Thread(s) per core: 2
|
|
||||||
Core(s) per socket: 6
|
|
||||||
Socket(s): 1
|
|
||||||
Stepping: 0
|
|
||||||
Frequency boost: enabled
|
|
||||||
CPU(s) scaling MHz: 67%
|
|
||||||
CPU max MHz: 4650.2920
|
|
||||||
CPU min MHz: 2200.0000
|
|
||||||
BogoMIPS: 7399.43
|
|
||||||
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt
|
|
||||||
pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16
|
|
||||||
sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefet
|
|
||||||
ch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba ibrs ib
|
|
||||||
pb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xget
|
|
||||||
bv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr rdpru wbnoinvd arat npt lbrv svm_lock nrip
|
|
||||||
_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif v_spec_ctrl umip pku ospk
|
|
||||||
e vaes vpclmulqdq rdpid overflow_recov succor smca fsrm
|
|
||||||
Virtualization features:
|
|
||||||
Virtualization: AMD-V
|
|
||||||
Caches (sum of all):
|
|
||||||
L1d: 192 KiB (6 instances)
|
|
||||||
L1i: 192 KiB (6 instances)
|
|
||||||
L2: 3 MiB (6 instances)
|
|
||||||
L3: 32 MiB (1 instance)
|
|
||||||
NUMA:
|
|
||||||
NUMA node(s): 1
|
|
||||||
NUMA node0 CPU(s): 0-11
|
|
||||||
Vulnerabilities:
|
|
||||||
Itlb multihit: Not affected
|
|
||||||
L1tf: Not affected
|
|
||||||
Mds: Not affected
|
|
||||||
Meltdown: Not affected
|
|
||||||
Mmio stale data: Not affected
|
|
||||||
Retbleed: Not affected
|
|
||||||
Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl
|
|
||||||
Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization
|
|
||||||
Spectre v2: Mitigation; Retpolines, IBPB conditional, IBRS_FW, STIBP always-on, RSB filling, PBRSB-eIBRS Not affected
|
|
||||||
Srbds: Not affected
|
|
||||||
Tsx async abort: Not affected
|
|
||||||
```
|
|
||||||
|
|
||||||
### Workstation at work
|
|
||||||
|
|
||||||
A beast. Many cores (which we don't use).
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
```
|
|
||||||
[rene@nixos:~]$ neofetch --stdout
|
|
||||||
rene@nixos
|
|
||||||
----------
|
|
||||||
OS: NixOS 23.05.2947.475d5ae2c4cb (Stoat) x86_64
|
|
||||||
Host: LENOVO 1038
|
|
||||||
Kernel: 6.1.46
|
|
||||||
Uptime: 26 mins
|
|
||||||
Packages: 5804 (nix-system), 566 (nix-user)
|
|
||||||
Shell: bash 5.2.15
|
|
||||||
Terminal: /dev/pts/2
|
|
||||||
CPU: Intel Xeon Gold 5218 (64) @ 3.900GHz
|
|
||||||
GPU: NVIDIA Quadro P620
|
|
||||||
GPU: NVIDIA Tesla M40
|
|
||||||
Memory: 1610MiB / 95247MiB
|
|
||||||
|
|
||||||
|
|
||||||
[rene@nixos:~]$ lscpu
|
|
||||||
Architecture: x86_64
|
|
||||||
CPU op-mode(s): 32-bit, 64-bit
|
|
||||||
Address sizes: 46 bits physical, 48 bits virtual
|
|
||||||
Byte Order: Little Endian
|
|
||||||
CPU(s): 64
|
|
||||||
On-line CPU(s) list: 0-63
|
|
||||||
Vendor ID: GenuineIntel
|
|
||||||
Model name: Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz
|
|
||||||
CPU family: 6
|
|
||||||
Model: 85
|
|
||||||
Thread(s) per core: 2
|
|
||||||
Core(s) per socket: 16
|
|
||||||
Socket(s): 2
|
|
||||||
Stepping: 7
|
|
||||||
CPU(s) scaling MHz: 57%
|
|
||||||
CPU max MHz: 3900,0000
|
|
||||||
CPU min MHz: 1000,0000
|
|
||||||
BogoMIPS: 4600,00
|
|
||||||
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs b
|
|
||||||
ts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_
|
|
||||||
deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb cat_l3 cdp_l3 invpcid_single intel_ppin ssbd mba ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpri
|
|
||||||
ority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid cqm mpx rdt_a avx512f avx512dq rdseed adx smap clflushopt clwb intel_pt avx512cd avx512bw avx512vl xsaveopt xs
|
|
||||||
avec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local dtherm ida arat pln pts hwp hwp_act_window hwp_epp hwp_pkg_req pku ospke avx512_vnni md_clear flush_l1d arch_capabi
|
|
||||||
lities
|
|
||||||
Virtualization features:
|
|
||||||
Virtualization: VT-x
|
|
||||||
Caches (sum of all):
|
|
||||||
L1d: 1 MiB (32 instances)
|
|
||||||
L1i: 1 MiB (32 instances)
|
|
||||||
L2: 32 MiB (32 instances)
|
|
||||||
L3: 44 MiB (2 instances)
|
|
||||||
NUMA:
|
|
||||||
NUMA node(s): 2
|
|
||||||
NUMA node0 CPU(s): 0-15,32-47
|
|
||||||
NUMA node1 CPU(s): 16-31,48-63
|
|
||||||
Vulnerabilities:
|
|
||||||
Gather data sampling: Mitigation; Microcode
|
|
||||||
Itlb multihit: KVM: Mitigation: VMX disabled
|
|
||||||
L1tf: Not affected
|
|
||||||
Mds: Not affected
|
|
||||||
Meltdown: Not affected
|
|
||||||
Mmio stale data: Mitigation; Clear CPU buffers; SMT vulnerable
|
|
||||||
Retbleed: Mitigation; Enhanced IBRS
|
|
||||||
Spec rstack overflow: Not affected
|
|
||||||
Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl
|
|
||||||
Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization
|
|
||||||
Spectre v2: Mitigation; Enhanced IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS SW sequence
|
|
||||||
Srbds: Not affected
|
|
||||||
Tsx async abort: Mitigation; TSX disabled
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Work Laptop
|
|
||||||
|
|
||||||
Very strange. It absolutely **LOVES** zap 🤣!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
```
|
|
||||||
➜ neofetch --stdout
|
|
||||||
rs@nixos
|
|
||||||
--------
|
|
||||||
OS: NixOS 23.05.2918.4cdad15f34e6 (Stoat) x86_64
|
|
||||||
Host: LENOVO 20TKS0W700
|
|
||||||
Kernel: 6.1.45
|
|
||||||
Uptime: 1 day, 4 hours, 50 mins
|
|
||||||
Packages: 6259 (nix-system), 267 (nix-user), 9 (flatpak)
|
|
||||||
Shell: bash 5.2.15
|
|
||||||
Resolution: 3840x1600, 3840x2160
|
|
||||||
DE: none+i3
|
|
||||||
WM: i3
|
|
||||||
Terminal: tmux
|
|
||||||
CPU: Intel i9-10885H (16) @ 5.300GHz
|
|
||||||
GPU: NVIDIA GeForce GTX 1650 Ti Mobile
|
|
||||||
Memory: 4525MiB / 31805MiB
|
|
||||||
|
|
||||||
|
|
||||||
➜ lscpu
|
|
||||||
Architecture: x86_64
|
|
||||||
CPU op-mode(s): 32-bit, 64-bit
|
|
||||||
Address sizes: 39 bits physical, 48 bits virtual
|
|
||||||
Byte Order: Little Endian
|
|
||||||
CPU(s): 16
|
|
||||||
On-line CPU(s) list: 0-15
|
|
||||||
Vendor ID: GenuineIntel
|
|
||||||
Model name: Intel(R) Core(TM) i9-10885H CPU @ 2.40GHz
|
|
||||||
CPU family: 6
|
|
||||||
Model: 165
|
|
||||||
Thread(s) per core: 2
|
|
||||||
Core(s) per socket: 8
|
|
||||||
Socket(s): 1
|
|
||||||
Stepping: 2
|
|
||||||
CPU(s) scaling MHz: 56%
|
|
||||||
CPU max MHz: 5300.0000
|
|
||||||
CPU min MHz: 800.0000
|
|
||||||
BogoMIPS: 4800.00
|
|
||||||
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust sgx bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp pku ospke sgx_lc md_clear flush_l1d arch_capabilities
|
|
||||||
Virtualization: VT-x
|
|
||||||
L1d cache: 256 KiB (8 instances)
|
|
||||||
L1i cache: 256 KiB (8 instances)
|
|
||||||
L2 cache: 2 MiB (8 instances)
|
|
||||||
L3 cache: 16 MiB (1 instance)
|
|
||||||
NUMA node(s): 1
|
|
||||||
NUMA node0 CPU(s): 0-15
|
|
||||||
Vulnerability Gather data sampling: Mitigation; Microcode
|
|
||||||
Vulnerability Itlb multihit: KVM: Mitigation: VMX disabled
|
|
||||||
Vulnerability L1tf: Not affected
|
|
||||||
Vulnerability Mds: Not affected
|
|
||||||
Vulnerability Meltdown: Not affected
|
|
||||||
Vulnerability Mmio stale data: Mitigation; Clear CPU buffers; SMT vulnerable
|
|
||||||
Vulnerability Retbleed: Mitigation; Enhanced IBRS
|
|
||||||
Vulnerability Spec rstack overflow: Not affected
|
|
||||||
Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl
|
|
||||||
Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization
|
|
||||||
Vulnerability Spectre v2: Mitigation; Enhanced IBRS, IBPB conditional, RSB filling, PBRSB-eIBRS SW sequence
|
|
||||||
Vulnerability Srbds: Mitigation; Microcode
|
|
||||||
Vulnerability Tsx async abort: Not affected
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
tag=$1
|
|
||||||
override=$2
|
|
||||||
|
|
||||||
if [ "$tag" == "--override" ] ; then
|
|
||||||
override=$tag
|
|
||||||
tag=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$tag" == "" ] ; then
|
|
||||||
tag=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
echo "Warning: no tag provided, using: >> $tag <<"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
git archive --format=tar.gz -o ${tag}.tar.gz --prefix=zap-$tag/ HEAD
|
|
||||||
|
|
||||||
git diff --quiet
|
|
||||||
|
|
||||||
if [ $? -ne 0 ] ; then
|
|
||||||
if [ "$override" == "--override" ] ; then
|
|
||||||
./zig-out/bin/pkghash -g --tag=$tag --template=doc/release-template.md
|
|
||||||
else
|
|
||||||
echo "WARNING: GIT WORKING TREE IS DIRTY!"
|
|
||||||
echo "If you want to get zig hash anyway, run:"
|
|
||||||
echo "./zig-out/bin/pkghash -g"
|
|
||||||
echo "or, with full-blown release-notes:"
|
|
||||||
echo "./zig-out/bin/pkghash -g --tag=$tag --template=doc/release-template.md"
|
|
||||||
echo ""
|
|
||||||
echo "To skip this message and do the pkghash thing anyway, supply the"
|
|
||||||
echo "--override parameter"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
./zig-out/bin/pkghash -g --tag=$tag --template=doc/release-template.md
|
|
||||||
fi
|
|
|
@ -1,101 +0,0 @@
|
||||||
# Self-hosting release packages until Zig master is fixed
|
|
||||||
|
|
||||||
Recently, GitHub started hosting release archives on a dedicated host
|
|
||||||
codeload.github.com. This is when the problems started. Back then, zig's package
|
|
||||||
manager was not expecting to be re-directed to a different URL. On top of that,
|
|
||||||
GitHub changed the redirected-to URLs so they wouldn't end in `.tar.gz` anymore.
|
|
||||||
|
|
||||||
Above issues were fixed but after some progress on `zig.http` related standard
|
|
||||||
library stuff, a similar error started impacting the package manager: parsing
|
|
||||||
long TLS responses has the [issue
|
|
||||||
ziglang/zig#15990](https://github.com/ziglang/zig/issues/15590).
|
|
||||||
|
|
||||||
So, here we are. Since this topic has come up often enough now, it deserves its
|
|
||||||
own doc.
|
|
||||||
|
|
||||||
## The workaround: self-hosting on localhost
|
|
||||||
|
|
||||||
My workaround is: not using https! The easiest way to do this, is:
|
|
||||||
|
|
||||||
- create the tar archive yourself
|
|
||||||
- start a python http server on the command line
|
|
||||||
- replace the URL in the build.zig.zon with a http and localhost one.
|
|
||||||
|
|
||||||
For simple packages, this is relatively easy. But zap itself has a
|
|
||||||
`build.zig.zon` that references its `facilio` dependency. For that reason, ZAP's
|
|
||||||
build.zig.zon also needs to change: to only reference localhost packages.
|
|
||||||
|
|
||||||
The consequence of changing build.zig.zon is: zap's package hash changes! -->
|
|
||||||
Any build.zig.zon that wants to use ZAP needs to change, too.
|
|
||||||
|
|
||||||
This is why, for the time being, I am always creating two releases,
|
|
||||||
a `release-0.0.n` one and `release-0.0.n-localhost` one, for each release.
|
|
||||||
|
|
||||||
|
|
||||||
So, while the TLS bug persists, you have to use the `-localhost` releases. The
|
|
||||||
procedure is:
|
|
||||||
|
|
||||||
- fetch zap's dependency `facilio` from GitHub
|
|
||||||
- fetch zap's `localhost` release from GitHub
|
|
||||||
- _(use the localhost URL and package hash in your build.zig)_
|
|
||||||
- start a local http server
|
|
||||||
- run zig build
|
|
||||||
- stop the http server
|
|
||||||
|
|
||||||
Here is an example for the `release-0.0.20-localhost` release which is the
|
|
||||||
current release at the time of writing:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ # get dependency required by zap
|
|
||||||
$ wget https://github.com/zigzap/facil.io/archive/refs/tags/zap-0.0.8.tar.gz
|
|
||||||
$ # get zap itself
|
|
||||||
$ wget https://github.com/zigzap/zap/archive/refs/tags/release-0.0.20-localhost.tar.gz
|
|
||||||
$ # start a http server on port 8000
|
|
||||||
$ python -m http.server
|
|
||||||
```
|
|
||||||
|
|
||||||
... and use the following in your build.zig.zon:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// zap release-0.0.20-localhost
|
|
||||||
.zap = .{
|
|
||||||
.url = "http://127.0.0.1/release-0.0.20-localhost.tar.gz",
|
|
||||||
.hash = "12204c663be7639e98af40ad738780014b18bcf35efbdb4c701aad51c7dec45abf4d",
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
After the first successful zig build, zig will have cached both dependencies,
|
|
||||||
the direct zap one and the transient facilio one, and you won't need to start an
|
|
||||||
HTTP server again until you want to update your dependencies.
|
|
||||||
|
|
||||||
|
|
||||||
## Building Release Packages yourself
|
|
||||||
|
|
||||||
- In your branch, replace `build.zig.zon` with `build.zig.zon.localhost`
|
|
||||||
- **make sure everything is committed and your branch is clean.** This is
|
|
||||||
essential for calculating the package hash.
|
|
||||||
- `zig build pkghash` if you haven't already.
|
|
||||||
- tag your release: `git tag MY_TAG`
|
|
||||||
- I recommend putting `localhost` in the tagname
|
|
||||||
- `./create-archive.sh MY_TAG`
|
|
||||||
|
|
||||||
The `create-archive.sh` script will spit out release notes that contain the
|
|
||||||
hashes, as well as a `MY_TAG.tar.gz`.
|
|
||||||
|
|
||||||
You can then host this via python HTTP server and proceed as if you had
|
|
||||||
downloaded it from github.
|
|
||||||
|
|
||||||
If all goes well, your dependend code should be able to use your freshly-built
|
|
||||||
zap release, depending on it via localhost URL in its `build.zig.zon`.
|
|
||||||
|
|
||||||
If not, fix bugs, rinse, and repeat.
|
|
||||||
|
|
||||||
You may want to push to your fork and create a GitHub 'localhost' release.
|
|
||||||
|
|
||||||
When you're happy with the release, you may consider replacing `build.zig.zon`
|
|
||||||
with the non-localhost version from the master branch. Commit it, make sure your
|
|
||||||
worktree is clean, and perform above steps again. This time, using a tag that
|
|
||||||
doesn't contain `localhost`. You can then push to your fork and create a release
|
|
||||||
for the future when zig's bug is fixed.
|
|
||||||
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
# Alternatives to released versions
|
|
||||||
|
|
||||||
|
|
||||||
## Using a tagged version
|
|
||||||
|
|
||||||
Go to [to the tags page](https://github.com/zigzap/zap/tags) to view all
|
|
||||||
available tagged versions of zap. From there, right click on the `tar.gz` link
|
|
||||||
to copy the URL to put into your `build.zig.zon`.
|
|
||||||
|
|
||||||
After changing the `.url` field, you will get an error like this at the next
|
|
||||||
attempt to `zig build`:
|
|
||||||
|
|
||||||
```
|
|
||||||
.../build.zig.zon:8:21: error: hash mismatch:
|
|
||||||
expected: 12205fd0b60720fb2a40d82118ee75c15cb5589bb9faf901c8a39a93551dd6253049,
|
|
||||||
found: 1220f4ea8be4a85716ae1362d34c077dca10f10d1baf9196fc890e658c56f78b7424
|
|
||||||
.hash = "12205fd0b60720fb2a40d82118ee75c15cb5589bb9faf901c8a39a93551dd6253049",
|
|
||||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** If you don't get this error, clean your global zig cache: `rm -fr
|
|
||||||
~/.cache/zig`. This shouldn't happen with current zig master anymore.
|
|
||||||
|
|
||||||
With the new URL, the old hash in the `build.zig.zon` is no longer valid. You
|
|
||||||
need to take the hash value displayed after `found: ` in the error message as
|
|
||||||
the `.hash` value in `build.zig.zon`.
|
|
||||||
|
|
||||||
|
|
||||||
## Using an arbitrary (last) commit
|
|
||||||
|
|
||||||
Use the same workflow as above for tags, excpept for the URL, use this schema:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
.url = "https://github.com/zigzap/zap/archive/[COMMIT-HASH].tar.gz",
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace `[COMMIT-HASH]` with the full commit hash as provided, e.g. by `git
|
|
||||||
log`.
|
|
||||||
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
# ZIG-CEPTION!
|
|
||||||
|
|
||||||
In ZAP, we have great zig-ception moment in the [middleware
|
|
||||||
example](../examples/middleware/middleware.zig). But first we need to introduce
|
|
||||||
one key function of `zap.Middleware`: **combining structs at comptime!**
|
|
||||||
|
|
||||||
## Combining structs at runtime
|
|
||||||
|
|
||||||
Here is how it is used in user-code:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// create a combined context struct
|
|
||||||
const Context = struct {
|
|
||||||
user: ?UserMiddleWare.User = null,
|
|
||||||
session: ?SessionMiddleWare.Session = null,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Why do we create combined structs? Because all our Middleware handler functions
|
|
||||||
need to receive a per-request context. But each wants their own data: the User
|
|
||||||
middleware might want to access a User struct, the Session middleware might want
|
|
||||||
a Session struct, and so on. So, which struct should we use in the prototype of
|
|
||||||
the "on_request" callback function? We could just use an `anyopaque` pointer.
|
|
||||||
That would solve the generic function prototype problem. But then everyone
|
|
||||||
implementing such a handler would need to cast this pointer back into - what?
|
|
||||||
Into the same type that the caller of the handler used. It gets really messy
|
|
||||||
when we continue this train of thought.
|
|
||||||
|
|
||||||
So, in ZAP, I opted for one Context type for all request handlers. Since ZAP is
|
|
||||||
a library, it cannot know what your preferred Context struct is. What it should
|
|
||||||
consist of. Therefore, it lets you combine all the structs your and maybe your
|
|
||||||
3rd parties's middleware components require - at comptime! And derive the
|
|
||||||
callback function prototype from that. If you look at the [middleware
|
|
||||||
example](../examples/middleware/middleware.zig), you'll notice, it's really
|
|
||||||
smooth to use.
|
|
||||||
|
|
||||||
**NOTE:** In your contexts, please also use OPTIONALS. They are set null at
|
|
||||||
context creation time. And will aid you in not shooting yourself in the foot
|
|
||||||
when accessing context fields that haven't been initialized - which may happen
|
|
||||||
when the order of your chain of components isn't perfect yet. 😉
|
|
||||||
|
|
||||||
## The zig-ception moment
|
|
||||||
|
|
||||||
Have a look at an excerpt of the example:
|
|
||||||
|
|
||||||
```zig
|
|
||||||
// create a combined context struct
|
|
||||||
const Context = struct {
|
|
||||||
user: ?UserMiddleWare.User = null,
|
|
||||||
session: ?SessionMiddleWare.Session = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
// we create a Handler type based on our Context
|
|
||||||
const Handler = zap.Middleware.Handler(Context);
|
|
||||||
|
|
||||||
//
|
|
||||||
// ZIG-CEPTION!!!
|
|
||||||
//
|
|
||||||
// Note how amazing zig is:
|
|
||||||
// - we create the "mixed" context based on the both middleware structs
|
|
||||||
// - we create the handler based on this context
|
|
||||||
// - we create the middleware structs based on the handler
|
|
||||||
// - which needs the context
|
|
||||||
// - which needs the middleware structs
|
|
||||||
// - ZIG-CEPTION!
|
|
||||||
|
|
||||||
// Example user middleware: puts user info into the context
|
|
||||||
const UserMiddleWare = struct {
|
|
||||||
handler: Handler,
|
|
||||||
|
|
||||||
// .. the UserMiddleWare depends on the handler
|
|
||||||
// which depends on the Context
|
|
||||||
// which depends on this UserMiddleWare struct
|
|
||||||
// ZIG-CEPTION!!!
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🤯
|
|
||||||
|
|
||||||
The comments in the code say it all.
|
|
||||||
|
|
||||||
**Isn't ZIG AMAZING?**
|
|
|
@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const zap = @import("zap");
|
const zap = @import("zap");
|
||||||
|
|
||||||
|
// The global Application Context
|
||||||
const MyContext = struct {
|
const MyContext = struct {
|
||||||
db_connection: []const u8,
|
db_connection: []const u8,
|
||||||
|
|
||||||
|
@ -13,12 +14,14 @@ const MyContext = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A very simple endpoint handling only GET requests
|
||||||
const SimpleEndpoint = struct {
|
const SimpleEndpoint = struct {
|
||||||
|
|
||||||
// Endpoint Interface part
|
// zap.App.Endpoint Interface part
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
|
error_strategy: zap.Endpoint.ErrorStrategy = .log_to_response,
|
||||||
|
|
||||||
|
// data specific for this endpoint
|
||||||
some_data: []const u8,
|
some_data: []const u8,
|
||||||
|
|
||||||
pub fn init(path: []const u8, data: []const u8) SimpleEndpoint {
|
pub fn init(path: []const u8, data: []const u8) SimpleEndpoint {
|
||||||
|
@ -28,12 +31,14 @@ const SimpleEndpoint = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle GET requests
|
||||||
pub fn get(e: *SimpleEndpoint, arena: Allocator, context: *MyContext, r: zap.Request) anyerror!void {
|
pub fn get(e: *SimpleEndpoint, arena: Allocator, context: *MyContext, r: zap.Request) anyerror!void {
|
||||||
|
const thread_id = std.Thread.getCurrentId();
|
||||||
|
|
||||||
r.setStatus(.ok);
|
r.setStatus(.ok);
|
||||||
|
|
||||||
const thread_id = std.Thread.getCurrentId();
|
// look, we use the arena allocator here -> no need to free the response_text later!
|
||||||
// look, we use the arena allocator here
|
// and we also just `try` it, not worrying about errors
|
||||||
// and we also just try it, not worrying about errors
|
|
||||||
const response_text = try std.fmt.allocPrint(
|
const response_text = try std.fmt.allocPrint(
|
||||||
arena,
|
arena,
|
||||||
\\Hello!
|
\\Hello!
|
||||||
|
@ -58,23 +63,29 @@ const SimpleEndpoint = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var my_context = MyContext.init("db connection established!");
|
// setup allocations
|
||||||
|
|
||||||
var gpa: std.heap.GeneralPurposeAllocator(.{
|
var gpa: std.heap.GeneralPurposeAllocator(.{
|
||||||
// just to be explicit
|
// just to be explicit
|
||||||
.thread_safe = true,
|
.thread_safe = true,
|
||||||
}) = .{};
|
}) = .{};
|
||||||
defer std.debug.print("\n\nLeaks detected: {}\n\n", .{gpa.deinit() != .ok});
|
defer std.debug.print("\n\nLeaks detected: {}\n\n", .{gpa.deinit() != .ok});
|
||||||
|
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
// create an app context
|
||||||
|
var my_context = MyContext.init("db connection established!");
|
||||||
|
|
||||||
|
// create an App instance
|
||||||
const App = zap.App.Create(MyContext);
|
const App = zap.App.Create(MyContext);
|
||||||
var app = try App.init(allocator, &my_context, .{});
|
var app = try App.init(allocator, &my_context, .{});
|
||||||
defer app.deinit();
|
defer app.deinit();
|
||||||
|
|
||||||
|
// create the endpoint
|
||||||
var my_endpoint = SimpleEndpoint.init("/", "some endpoint specific data");
|
var my_endpoint = SimpleEndpoint.init("/", "some endpoint specific data");
|
||||||
|
|
||||||
|
// register the endpoint with the app
|
||||||
try app.register(&my_endpoint);
|
try app.register(&my_endpoint);
|
||||||
|
|
||||||
|
// listen on the network
|
||||||
try app.listen(.{
|
try app.listen(.{
|
||||||
.interface = "0.0.0.0",
|
.interface = "0.0.0.0",
|
||||||
.port = 3000,
|
.port = 3000,
|
||||||
|
|
12
flake.lock
generated
|
@ -70,11 +70,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741037377,
|
"lastModified": 1743259260,
|
||||||
"narHash": "sha256-SvtvVKHaUX4Owb+PasySwZsoc5VUeTf1px34BByiOxw=",
|
"narHash": "sha256-ArWLUgRm1tKHiqlhnymyVqi5kLNCK5ghvm06mfCl4QY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "02032da4af073d0f6110540c8677f16d4be0117f",
|
"rev": "eb0e0f21f15c559d2ac7633dc81d079d1caf5f5f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -145,11 +145,11 @@
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741263138,
|
"lastModified": 1743250246,
|
||||||
"narHash": "sha256-qlX8tgtZMTSOEeAM8AmC7K6mixgYOguhl/xLj5xQrXc=",
|
"narHash": "sha256-gVFyxsxfqnEXSldeeURim7RRZGwPX4f/egLcSC7CXec=",
|
||||||
"owner": "mitchellh",
|
"owner": "mitchellh",
|
||||||
"repo": "zig-overlay",
|
"repo": "zig-overlay",
|
||||||
"rev": "627055069ee1409e8c9be7bcc533e8823fb87b18",
|
"rev": "b0da956a6db25564d0ee461e669fb07a348d2528",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
24
flake.nix
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
# nixpkgs.url = "github:nixos/nixpkgs/release-23.05";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
# required for latest zig
|
# required for latest zig
|
||||||
|
@ -38,28 +37,10 @@
|
||||||
in rec {
|
in rec {
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
# TODO: re-enable this once it is fixed: zigpkgs."0.14.0"
|
zigpkgs."0.14.0"
|
||||||
zigpkgs.master
|
|
||||||
bat
|
bat
|
||||||
wrk
|
wrk
|
||||||
python3
|
|
||||||
python3Packages.sanic
|
|
||||||
python3Packages.setuptools
|
|
||||||
python3Packages.matplotlib
|
|
||||||
poetry
|
|
||||||
poetry
|
|
||||||
pkgs.rustc
|
|
||||||
pkgs.cargo
|
|
||||||
pkgs.gcc
|
|
||||||
pkgs.rustfmt
|
|
||||||
pkgs.clippy
|
|
||||||
pkgs.go
|
|
||||||
pkgs.gotools
|
|
||||||
pkgs.gopls
|
|
||||||
pkgs.golint
|
|
||||||
|
|
||||||
pkgs.dotnet-sdk_8
|
|
||||||
pkgs.dotnet-runtime_8
|
|
||||||
pkgs.zlib
|
pkgs.zlib
|
||||||
pkgs.icu
|
pkgs.icu
|
||||||
pkgs.openssl
|
pkgs.openssl
|
||||||
|
@ -85,9 +66,8 @@
|
||||||
|
|
||||||
devShells.build = pkgs.mkShell {
|
devShells.build = pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
# zigpkgs."0.14.0"
|
zigpkgs."0.14.0"
|
||||||
zigpkgs.master
|
zigpkgs.master
|
||||||
pkgs.openssl
|
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
|
|
100
introducing.md
|
@ -1,100 +0,0 @@
|
||||||
# Introducing ⚡zap⚡ - blazingly fast backends in zig
|
|
||||||
|
|
||||||
Zap is intended to become my [zig](https://ziglang.org) replacement for the kind of REST APIs I used to write in [python](https://python.org) with [Flask](https://flask.palletsprojects.com) and [mongodb](https://www.mongodb.com), etc. It can be considered to be a microframework for web applications.
|
|
||||||
|
|
||||||
What I need for that is a blazingly fast, robust HTTP server that I can use with zig. While facil.io supports TLS, I don't care about HTTPS support. In production, I use [nginx](https://www.nginx.com) as a reverse proxy anyway.
|
|
||||||
|
|
||||||
Zap wraps and patches [facil.io - the C web application framework](https://facil.io).
|
|
||||||
|
|
||||||
At the time of writing, ZAP is only a few days old and aims to be:
|
|
||||||
|
|
||||||
- **robust**
|
|
||||||
- **fast**
|
|
||||||
- **minimal**
|
|
||||||
|
|
||||||
**⚡ZAP⚡ IS SUPER ALPHA**
|
|
||||||
|
|
||||||
_Under the hood, everything is super robust and fast. My zig wrappers are fresh, juicy, and alpha._
|
|
||||||
|
|
||||||
Here's what works:
|
|
||||||
|
|
||||||
- **Super easy build process**: zap's `build.zig` fetches facilio's git sub-module, applies a patch to its logging for microsecond precision, and then builds and optionally runs everything.
|
|
||||||
- _tested on Linux and macOS (arm, M1)_
|
|
||||||
- **[hello](https://github.com/renerocksai/zap/blob/master/examples/hello/hello.zig)**: welcomes you with some static HTML
|
|
||||||
- **[routes](https://github.com/renerocksai/zap/blob/master/examples/routes/routes.zig)**: a super easy example dispatching on the HTTP path
|
|
||||||
- **[serve](https://github.com/renerocksai/zap/blob/master/examples/serve/serve.zig)**: the traditional static web server with optional dynamic request handling
|
|
||||||
- **[hello_json](https://github.com/renerocksai/zap/blob/master/examples/hello_json/hello_json.zig)**: serves you json dependent on HTTP path
|
|
||||||
- **[endpoint](https://github.com/renerocksai/zap/blob/master/examples/endpoint/)**: a simple JSON REST API example featuring a `/users` endpoint for PUTting/DELETE-ing/GET-ting/POST-ing and listing users, together with a static HTML and JavaScript frontend to play with.
|
|
||||||
|
|
||||||
If you want to take it for a quick spin:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ git clone https://github.com/renerocksai/zap.git
|
|
||||||
$ cd zap
|
|
||||||
$ zig build run-hello
|
|
||||||
$ # open http://localhost:3000 in your browser
|
|
||||||
```
|
|
||||||
|
|
||||||
See [the README](https://github.com/renerocksai/zap) for how easy it is to get started, how to run the examples, and how to use zap in your own projects.
|
|
||||||
|
|
||||||
I'll continue wrapping more of facil.io's functionality and adding stuff to zap to a point where I can use it as the JSON REST API backend for real research projects, serving thousands of concurrent clients. Now that the endpoint example works, ZAP has actually become pretty usable to me.
|
|
||||||
|
|
||||||
**Side-note:** It never ceases to amaze me how productive I can be in zig, eventhough I am still considering myself to be a newbie. Sometimes, it's almost like writing python but with all the nice speed and guarantees that zig gives you. Also, the C integration abilities of zig are just phenomenal! I am super excited about zig's future!
|
|
||||||
|
|
||||||
Now, on to the guiding principles of Zap.
|
|
||||||
|
|
||||||
## robust
|
|
||||||
|
|
||||||
A common recommendation for doing web stuff in zig is to write the actual HTTP server in Go, and use zig for the real work. While there is a selection of notable and cool HTTP server implementations written in zig out there, at the time of writing, most of them seem to a) depend on zig's async facilities which are unsupported until ca. April 2023 when async will return to the self-hosted compiler, and b) have not matured to a point where **I** feel safe using them in production. These are just my opionions and they could be totally wrong though.
|
|
||||||
|
|
||||||
However, when I conduct my next online research experiment with thousands of concurrent clients, I cannot afford to run into potential maturity-problems of the HTTP server. These projects typically feature a you-get-one-shot process with little room for errors or re-tries.
|
|
||||||
|
|
||||||
With zap, if something should go wrong, at least I'd be close enough to the source-code to, hopefully, be able to fix it in production. With that out of the way, I am super confident that facil.io is very mature compared to many of the alternatives. My `wrk` tests also look promising.
|
|
||||||
|
|
||||||
I intend to add app-specific performance tests, e.g. stress-testing the endpoint example, to make sure the zap endpoint framework is able to sustain a high load without running into performance or memory problems. That will be interesting.
|
|
||||||
|
|
||||||
|
|
||||||
## ⚡blazingly fast⚡
|
|
||||||
|
|
||||||
Claiming to be blazingly fast is the new black. At least, zap doesn't slow you down and if your server performs poorly, it's probably not exactly zap's fault. Zap relies on the [facil.io](https://facil.io) framework and so it can't really claim any performance fame for itself. In this initial implementation of zap, I didn't care about optimizations at all.
|
|
||||||
|
|
||||||
But, how fast is it? Being blazingly fast is relative. When compared with a simple GO HTTP server, a simple zig zap HTTP server performed really good on my machine:
|
|
||||||
|
|
||||||
- zig zap was nearly 30% faster than GO
|
|
||||||
- zig zap had over 50% more throughput than GO
|
|
||||||
|
|
||||||
I intentionally only tested static HTTP output, as that seemed to be the best common ground of all test subjects to me. The measurements were for just getting a ballpark baseline anyway.
|
|
||||||
|
|
||||||
**Update**: I was intrigued comparing to a basic rust HTTP server. Unfortunately, knowing nothing at all about rust, I couldn't find a simple, just-a-few-lines one like in Go and Python right away and hence tried to go for the one in the book [The Rust Programming Language](https://doc.rust-lang.org/book/ch20-00-final-project-a-web-server.html). Wanting it to be of a somewhat fair comparison, I opted for the multi-threaded example. It didn't work out-of-the-book, but I got it to work (essentially, by commenting out all "print" statements) and changed it to not read files but outputting static text just like the other examples. **Maybe someone with rust experience** can have a look at my [wrk/rust/hello](wrk/rust/hello) code and tell me why it is surprisingly 'slow', as I expected it to be faster than or at least on-par with the basic Go example. I'll enable the GitHub discussions for this matter. My suspicion is bad performance of the mutexes.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
So, being somewhere in the ballpark of basic GO performance, zig zap seems to be ... of reasonable performance 😎.
|
|
||||||
|
|
||||||
See more details in [blazingly-fast.md](https://github.com/renerocksai/zap/blob/master/blazingly-fast.md).
|
|
||||||
|
|
||||||
## minimal
|
|
||||||
|
|
||||||
Zap is minimal by necessity. I only (have time to) add what I need - for serving REST APIs and HTML. The primary use-case are frontends that I wrote that communicate with my APIs. Hence, the focus is more on getting stuff done rather than conforming to every standard there is. Even though facilio is able to support TLS, I don't care about that - at least for now. Also, if you present `404 - File not found` as human-readable HTML to the user, nobody forces you to also set the status code to 404, so it can be OK to spare those nanoseconds. Gotta go fast!
|
|
||||||
|
|
||||||
Facilio comes with Mustache parsing, TLS via third-party libs, websockets, redis support, concurrency stuff, Base64 support, logging facilities, pub/sub / cluster messages API, hash algorithm implementations, its own memory allocator, and so forth. It is really an amazing project!
|
|
||||||
|
|
||||||
On the lower level, you can use all of the above by working with `zap.C`. I'll zig-wrap what I need for my projects first, before adding more fancy stuff.
|
|
||||||
|
|
||||||
Also, there are nice and well-typed zig implementations for some of the above extra functionalities, and zap-wrapping them needs careful consideration. E.g. it might not be worth the extra effort to wrap facil.io's mustache support when there is a good zig alternative already. Performance / out-of-the-box integration might be arguments pro wrapping them in zap.
|
|
||||||
|
|
||||||
## wrapping up - zig is WYSIWYG code
|
|
||||||
|
|
||||||
I am super excited about both zig and zap's future. I am still impressed by how easy it is to integrate a C codebase into a zig project, then benefiting from and building on top of battle-tested high-performance C code. Additionally, with zig you get C-like performance with almost Python-like comfort. And you can be sure no exception is trying to get you when you least expect it. No hidden allocations, no hidden control-flows, how cool is that? **WYSIWYG code!**
|
|
||||||
|
|
||||||
Provided that the incorporated C code is well-written and -tested, WYSIWYG even holds mostly true for combined Zig and C projects.
|
|
||||||
|
|
||||||
You can truly build on the soulders of giants here. Mind you, it took me less than a week to arrive at the current state of zap where I am confident that I can already use it to write the one or other REST API with it and, after stress-testing, just move it into production - from merely researching Zig and C web frameworks a few days ago.
|
|
||||||
|
|
||||||
Oh, and have I mentioned Zig's built-in build system and testing framework? Those are both super amazing and super convenient. `zig build` is so much more useful than `make` (which I quite like to be honest). And `zig test` is just amazing, too. Zig's physical code layout: which file is located where and how can it be built, imported, tested - it all makes so much sense. Such a coherent, pleasant experience.
|
|
||||||
|
|
||||||
Looking forward, I am also tempted to try adding some log-and-replay facilities as a kind of backup for when things go wrong. I wouldn't be confident to attemt such things in C because I'd view them as being too much work; too much could go wrong. But with Zig, I am rather excited about the possibilities that open up and eager to try such things.
|
|
||||||
|
|
||||||
For great justice!
|
|
35
shell.nix
|
@ -1,35 +0,0 @@
|
||||||
{
|
|
||||||
pkgs ? import <nixpkgs> {
|
|
||||||
overlays = [
|
|
||||||
(import (builtins.fetchTarball {
|
|
||||||
# url = https://github.com/nix-community/neovim-nightly-overlay/archive/master.tar.gz;
|
|
||||||
url = https://github.com/nix-community/neovim-nightly-overlay/archive/72ff8b1ca0331a8735c1eeaefb95c12dfe21d30a.tar.gz;
|
|
||||||
}))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} :
|
|
||||||
pkgs.mkShell {
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.neovim-nightly
|
|
||||||
pkgs.bat
|
|
||||||
pkgs.wrk
|
|
||||||
pkgs.python3
|
|
||||||
pkgs.rustc
|
|
||||||
pkgs.cargo
|
|
||||||
pkgs.gcc
|
|
||||||
pkgs.rustfmt
|
|
||||||
pkgs.clippy
|
|
||||||
];
|
|
||||||
|
|
||||||
buildInputs = [
|
|
||||||
pkgs.go
|
|
||||||
pkgs.gotools
|
|
||||||
pkgs.gopls
|
|
||||||
# pkgs.go-outline
|
|
||||||
# pkgs.gocode
|
|
||||||
# pkgs.gopkgs
|
|
||||||
# pkgs.gocode-gomod
|
|
||||||
# pkgs.godef
|
|
||||||
pkgs.golint
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
# axum
|
|
||||||
|
|
||||||
```console
|
|
||||||
zap on newwrk [$!?] via ↯ v0.11.0-dev.2837+b55b8e774 via impure (nix-shell)
|
|
||||||
➜ wrk/measure.sh axum
|
|
||||||
Finished release [optimized] target(s) in 0.05s
|
|
||||||
========================================================================
|
|
||||||
axum
|
|
||||||
========================================================================
|
|
||||||
Running 10s test @ http://127.0.0.1:3000
|
|
||||||
4 threads and 400 connections
|
|
||||||
Thread Stats Avg Stdev Max +/- Stdev
|
|
||||||
Latency 527.01us 260.08us 8.47ms 74.31%
|
|
||||||
Req/Sec 151.11k 4.06k 166.63k 71.25%
|
|
||||||
Latency Distribution
|
|
||||||
50% 518.00us
|
|
||||||
75% 644.00us
|
|
||||||
90% 811.00us
|
|
||||||
99% 1.39ms
|
|
||||||
6014492 requests in 10.01s, 768.61MB read
|
|
||||||
Requests/sec: 600582.38
|
|
||||||
Transfer/sec: 76.75MB
|
|
||||||
|
|
||||||
zap on newwrk [$!?] via ↯ v0.11.0-dev.2837+b55b8e774 via impure (nix-shell) took 11s
|
|
||||||
➜ wrk/measure.sh axum
|
|
||||||
Finished release [optimized] target(s) in 0.05s
|
|
||||||
========================================================================
|
|
||||||
axum
|
|
||||||
========================================================================
|
|
||||||
Running 10s test @ http://127.0.0.1:3000
|
|
||||||
4 threads and 400 connections
|
|
||||||
Thread Stats Avg Stdev Max +/- Stdev
|
|
||||||
Latency 534.89us 280.25us 7.37ms 76.81%
|
|
||||||
Req/Sec 150.03k 4.26k 162.67k 72.75%
|
|
||||||
Latency Distribution
|
|
||||||
50% 520.00us
|
|
||||||
75% 647.00us
|
|
||||||
90% 831.00us
|
|
||||||
99% 1.50ms
|
|
||||||
5969526 requests in 10.01s, 762.86MB read
|
|
||||||
Requests/sec: 596134.58
|
|
||||||
Transfer/sec: 76.18MB
|
|
||||||
|
|
||||||
zap on newwrk [$!?] via ↯ v0.11.0-dev.2837+b55b8e774 via impure (nix-shell) took 11s
|
|
||||||
➜ wrk/measure.sh axum
|
|
||||||
Finished release [optimized] target(s) in 0.05s
|
|
||||||
========================================================================
|
|
||||||
axum
|
|
||||||
========================================================================
|
|
||||||
Running 10s test @ http://127.0.0.1:3000
|
|
||||||
4 threads and 400 connections
|
|
||||||
Thread Stats Avg Stdev Max +/- Stdev
|
|
||||||
Latency 519.96us 269.86us 11.92ms 76.98%
|
|
||||||
Req/Sec 151.29k 4.32k 164.52k 69.75%
|
|
||||||
Latency Distribution
|
|
||||||
50% 509.00us
|
|
||||||
75% 635.00us
|
|
||||||
90% 800.00us
|
|
||||||
99% 1.41ms
|
|
||||||
6021199 requests in 10.01s, 769.46MB read
|
|
||||||
Requests/sec: 601482.51
|
|
||||||
Transfer/sec: 76.86MB
|
|
||||||
|
|
||||||
zap on newwrk [$!?] via ↯ v0.11.0-dev.2837+b55b8e774 via impure (nix-shell) took 11s
|
|
||||||
➜
|
|
||||||
```
|
|
||||||
|
|
||||||
# sanic
|
|
||||||
|
|
||||||
|
|
752
wrk/axum/hello-axum/Cargo.lock
generated
|
@ -1,752 +0,0 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-trait"
|
|
||||||
version = "0.1.68"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.15",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "axum"
|
|
||||||
version = "0.5.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"axum-core",
|
|
||||||
"bitflags",
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"hyper",
|
|
||||||
"itoa",
|
|
||||||
"matchit",
|
|
||||||
"memchr",
|
|
||||||
"mime",
|
|
||||||
"percent-encoding",
|
|
||||||
"pin-project-lite",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_urlencoded",
|
|
||||||
"sync_wrapper",
|
|
||||||
"tokio",
|
|
||||||
"tower",
|
|
||||||
"tower-http",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "axum-core"
|
|
||||||
version = "0.2.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"mime",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytes"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fnv"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "form_urlencoded"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
|
||||||
dependencies = [
|
|
||||||
"percent-encoding",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-channel"
|
|
||||||
version = "0.3.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-core"
|
|
||||||
version = "0.3.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"pin-project-lite",
|
|
||||||
"pin-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hello-axum"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"axum",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.2.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http"
|
|
||||||
version = "0.2.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"fnv",
|
|
||||||
"itoa",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body"
|
|
||||||
version = "0.4.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"http",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-range-header"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "httparse"
|
|
||||||
version = "1.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "httpdate"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper"
|
|
||||||
version = "0.14.26"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"httparse",
|
|
||||||
"httpdate",
|
|
||||||
"itoa",
|
|
||||||
"pin-project-lite",
|
|
||||||
"socket2",
|
|
||||||
"tokio",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
"want",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itoa"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.142"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matchit"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mime"
|
|
||||||
version = "0.3.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio"
|
|
||||||
version = "0.8.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"wasi",
|
|
||||||
"windows-sys 0.45.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num_cpus"
|
|
||||||
version = "1.15.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.17.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
"parking_lot_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking_lot_core"
|
|
||||||
version = "0.9.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"redox_syscall",
|
|
||||||
"smallvec",
|
|
||||||
"windows-sys 0.45.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "percent-encoding"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project"
|
|
||||||
version = "1.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
|
|
||||||
dependencies = [
|
|
||||||
"pin-project-internal",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-internal"
|
|
||||||
version = "1.0.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.56"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.26"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.160"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_json"
|
|
||||||
version = "1.0.96"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_urlencoded"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
|
||||||
dependencies = [
|
|
||||||
"form_urlencoded",
|
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "signal-hook-registry"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "smallvec"
|
|
||||||
version = "1.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "socket2"
|
|
||||||
version = "0.4.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sync_wrapper"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio"
|
|
||||||
version = "1.28.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"bytes",
|
|
||||||
"libc",
|
|
||||||
"mio",
|
|
||||||
"num_cpus",
|
|
||||||
"parking_lot",
|
|
||||||
"pin-project-lite",
|
|
||||||
"signal-hook-registry",
|
|
||||||
"socket2",
|
|
||||||
"tokio-macros",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-macros"
|
|
||||||
version = "2.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.15",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"pin-project",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-http"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"bytes",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"http-range-header",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tower",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-layer"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-service"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing"
|
|
||||||
version = "0.1.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"log",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-core"
|
|
||||||
version = "0.1.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "try-lock"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "want"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"try-lock",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.45.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.42.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.42.2",
|
|
||||||
"windows_aarch64_msvc 0.42.2",
|
|
||||||
"windows_i686_gnu 0.42.2",
|
|
||||||
"windows_i686_msvc 0.42.2",
|
|
||||||
"windows_x86_64_gnu 0.42.2",
|
|
||||||
"windows_x86_64_gnullvm 0.42.2",
|
|
||||||
"windows_x86_64_msvc 0.42.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.48.0",
|
|
||||||
"windows_aarch64_msvc 0.48.0",
|
|
||||||
"windows_i686_gnu 0.48.0",
|
|
||||||
"windows_i686_msvc 0.48.0",
|
|
||||||
"windows_x86_64_gnu 0.48.0",
|
|
||||||
"windows_x86_64_gnullvm 0.48.0",
|
|
||||||
"windows_x86_64_msvc 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.42.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|
|
@ -1,129 +0,0 @@
|
||||||
# 60 dependencies!!!
|
|
||||||
|
|
||||||
```console
|
|
||||||
➜ cargo build
|
|
||||||
Updating crates.io index
|
|
||||||
Downloaded percent-encoding v2.2.0
|
|
||||||
Downloaded sync_wrapper v0.1.2
|
|
||||||
Downloaded async-trait v0.1.68
|
|
||||||
Downloaded unicode-ident v1.0.8
|
|
||||||
Downloaded tracing v0.1.37
|
|
||||||
Downloaded mime v0.3.17
|
|
||||||
Downloaded http-body v0.4.5
|
|
||||||
Downloaded bytes v1.4.0
|
|
||||||
Downloaded httpdate v1.0.2
|
|
||||||
Downloaded httparse v1.8.0
|
|
||||||
Downloaded http v0.2.9
|
|
||||||
Downloaded lock_api v0.4.9
|
|
||||||
Downloaded num_cpus v1.15.0
|
|
||||||
Downloaded parking_lot v0.12.1
|
|
||||||
Downloaded pin-project v1.0.12
|
|
||||||
Downloaded parking_lot_core v0.9.7
|
|
||||||
Downloaded form_urlencoded v1.1.0
|
|
||||||
Downloaded log v0.4.17
|
|
||||||
Downloaded memchr v2.5.0
|
|
||||||
Downloaded matchit v0.5.0
|
|
||||||
Downloaded pin-project-lite v0.2.9
|
|
||||||
Downloaded scopeguard v1.1.0
|
|
||||||
Downloaded pin-utils v0.1.0
|
|
||||||
Downloaded once_cell v1.17.1
|
|
||||||
Downloaded serde_urlencoded v0.7.1
|
|
||||||
Downloaded pin-project-internal v1.0.12
|
|
||||||
Downloaded ryu v1.0.13
|
|
||||||
Downloaded quote v1.0.26
|
|
||||||
Downloaded proc-macro2 v1.0.56
|
|
||||||
Downloaded signal-hook-registry v1.4.1
|
|
||||||
Downloaded socket2 v0.4.9
|
|
||||||
Downloaded tower-service v0.3.2
|
|
||||||
Downloaded tower-layer v0.3.2
|
|
||||||
Downloaded tower v0.4.13
|
|
||||||
Downloaded autocfg v1.1.0
|
|
||||||
Downloaded syn v2.0.15
|
|
||||||
Downloaded try-lock v0.2.4
|
|
||||||
Downloaded futures-task v0.3.28
|
|
||||||
Downloaded futures-core v0.3.28
|
|
||||||
Downloaded fnv v1.0.7
|
|
||||||
Downloaded bitflags v1.3.2
|
|
||||||
Downloaded futures-util v0.3.28
|
|
||||||
Downloaded hyper v0.14.26
|
|
||||||
Downloaded axum v0.5.17
|
|
||||||
Downloaded smallvec v1.10.0
|
|
||||||
Downloaded want v0.3.0
|
|
||||||
Downloaded axum-core v0.2.9
|
|
||||||
Downloaded mio v0.8.6
|
|
||||||
Downloaded tokio-macros v2.1.0
|
|
||||||
Downloaded serde v1.0.160
|
|
||||||
Downloaded tower-http v0.3.5
|
|
||||||
Downloaded serde_json v1.0.96
|
|
||||||
Downloaded syn v1.0.109
|
|
||||||
Downloaded futures-channel v0.3.28
|
|
||||||
Downloaded tracing-core v0.1.30
|
|
||||||
Downloaded itoa v1.0.6
|
|
||||||
Downloaded cfg-if v1.0.0
|
|
||||||
Downloaded http-range-header v0.3.0
|
|
||||||
Downloaded libc v0.2.142
|
|
||||||
Downloaded tokio v1.28.0
|
|
||||||
Downloaded 60 crates (4.1 MB) in 2.81s
|
|
||||||
Compiling proc-macro2 v1.0.56
|
|
||||||
Compiling unicode-ident v1.0.8
|
|
||||||
Compiling quote v1.0.26
|
|
||||||
Compiling libc v0.2.142
|
|
||||||
Compiling cfg-if v1.0.0
|
|
||||||
Compiling autocfg v1.1.0
|
|
||||||
Compiling log v0.4.17
|
|
||||||
Compiling futures-core v0.3.28
|
|
||||||
Compiling pin-project-lite v0.2.9
|
|
||||||
Compiling bytes v1.4.0
|
|
||||||
Compiling itoa v1.0.6
|
|
||||||
Compiling futures-task v0.3.28
|
|
||||||
Compiling parking_lot_core v0.9.7
|
|
||||||
Compiling futures-util v0.3.28
|
|
||||||
Compiling syn v1.0.109
|
|
||||||
Compiling smallvec v1.10.0
|
|
||||||
Compiling scopeguard v1.1.0
|
|
||||||
Compiling once_cell v1.17.1
|
|
||||||
Compiling pin-utils v0.1.0
|
|
||||||
Compiling fnv v1.0.7
|
|
||||||
Compiling serde v1.0.160
|
|
||||||
Compiling tower-service v0.3.2
|
|
||||||
Compiling futures-channel v0.3.28
|
|
||||||
Compiling httparse v1.8.0
|
|
||||||
Compiling tower-layer v0.3.2
|
|
||||||
Compiling async-trait v0.1.68
|
|
||||||
Compiling try-lock v0.2.4
|
|
||||||
Compiling serde_json v1.0.96
|
|
||||||
Compiling ryu v1.0.13
|
|
||||||
Compiling memchr v2.5.0
|
|
||||||
Compiling percent-encoding v2.2.0
|
|
||||||
Compiling http-range-header v0.3.0
|
|
||||||
Compiling httpdate v1.0.2
|
|
||||||
Compiling bitflags v1.3.2
|
|
||||||
Compiling mime v0.3.17
|
|
||||||
Compiling sync_wrapper v0.1.2
|
|
||||||
Compiling matchit v0.5.0
|
|
||||||
Compiling http v0.2.9
|
|
||||||
Compiling tracing-core v0.1.30
|
|
||||||
Compiling form_urlencoded v1.1.0
|
|
||||||
Compiling lock_api v0.4.9
|
|
||||||
Compiling tokio v1.28.0
|
|
||||||
Compiling want v0.3.0
|
|
||||||
Compiling tracing v0.1.37
|
|
||||||
Compiling syn v2.0.15
|
|
||||||
Compiling http-body v0.4.5
|
|
||||||
Compiling num_cpus v1.15.0
|
|
||||||
Compiling socket2 v0.4.9
|
|
||||||
Compiling signal-hook-registry v1.4.1
|
|
||||||
Compiling mio v0.8.6
|
|
||||||
Compiling parking_lot v0.12.1
|
|
||||||
Compiling serde_urlencoded v0.7.1
|
|
||||||
Compiling tokio-macros v2.1.0
|
|
||||||
Compiling pin-project-internal v1.0.12
|
|
||||||
Compiling axum-core v0.2.9
|
|
||||||
Compiling pin-project v1.0.12
|
|
||||||
Compiling tower v0.4.13
|
|
||||||
Compiling hyper v0.14.26
|
|
||||||
Compiling tower-http v0.3.5
|
|
||||||
Compiling axum v0.5.17
|
|
||||||
Compiling hello-axum v0.1.0 (/home/rs/code/github.com/zigzap/zap/wrk/axum/hello-axum)
|
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 53.19s
|
|
||||||
```
|
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "hello-axum"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
axum = "0.5"
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
|
|
@ -1,29 +0,0 @@
|
||||||
use axum::{routing::get, Router};
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
// Route all requests on "/" endpoint to anonymous handler.
|
|
||||||
//
|
|
||||||
// A handler is an async function which returns something that implements
|
|
||||||
// `axum::response::IntoResponse`.
|
|
||||||
|
|
||||||
// A closure or a function can be used as handler.
|
|
||||||
|
|
||||||
let app = Router::new().route("/", get(handler));
|
|
||||||
// Router::new().route("/", get(|| async { "Hello, world!" }));
|
|
||||||
|
|
||||||
// Address that server will bind to.
|
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
|
||||||
|
|
||||||
// Use `hyper::server::Server` which is re-exported through `axum::Server` to serve the app.
|
|
||||||
axum::Server::bind(&addr)
|
|
||||||
// Hyper server takes a make service.
|
|
||||||
.serve(app.into_make_service())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handler() -> &'static str {
|
|
||||||
"Hello from axum!!"
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn build(b: *std.Build) void {
|
|
||||||
const target = b.standardTargetOptions(.{});
|
|
||||||
|
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
|
||||||
.name = "cpp-beast",
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
exe.addIncludePath(.{ .path = "." });
|
|
||||||
exe.addCSourceFiles(&.{"main.cpp"}, &.{
|
|
||||||
"-Wall",
|
|
||||||
"-Wextra",
|
|
||||||
"-Wshadow",
|
|
||||||
});
|
|
||||||
const libasio_dep = b.dependency("beast", .{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
const libasio = libasio_dep.artifact("beast");
|
|
||||||
for (libasio.include_dirs.items) |include| {
|
|
||||||
exe.include_dirs.append(include) catch {};
|
|
||||||
}
|
|
||||||
exe.linkLibrary(libasio);
|
|
||||||
exe.linkLibCpp();
|
|
||||||
|
|
||||||
b.installArtifact(exe);
|
|
||||||
const run_cmd = b.addRunArtifact(exe);
|
|
||||||
run_cmd.step.dependOn(b.getInstallStep());
|
|
||||||
if (b.args) |args| {
|
|
||||||
run_cmd.addArgs(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
const run_step = b.step("run", "Run C++ Http Server");
|
|
||||||
run_step.dependOn(&run_cmd.step);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
.{
|
|
||||||
.name = "cpp-beast",
|
|
||||||
.version = "0.1.0",
|
|
||||||
.dependencies = .{
|
|
||||||
.beast = .{
|
|
||||||
.url = "https://github.com/kassane/beast/archive/df69ba4d48fbe874730f6a28e9528d9ef7a9547c.tar.gz",
|
|
||||||
.hash = "1220548f8727394522081ab48ed2f7111c20fa5f051ff287ec3c3f82340faa5d68c2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
Hello from C++!!
|
|
|
@ -1,80 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <boost/beast.hpp>
|
|
||||||
#include <boost/asio/thread_pool.hpp>
|
|
||||||
|
|
||||||
namespace beast = boost::beast;
|
|
||||||
namespace http = beast::http;
|
|
||||||
namespace net = boost::asio;
|
|
||||||
using tcp = net::ip::tcp;
|
|
||||||
|
|
||||||
std::string read_html_file(const std::string& file_path) {
|
|
||||||
std::ifstream file(file_path);
|
|
||||||
if (!file) {
|
|
||||||
return "File not found: " + file_path;
|
|
||||||
}
|
|
||||||
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
||||||
file.close();
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_client(tcp::socket socket, const std::string& msg) {
|
|
||||||
try {
|
|
||||||
// Construct an HTTP response with the HTML content
|
|
||||||
http::response<http::string_body> response;
|
|
||||||
response.version(11);
|
|
||||||
response.result(http::status::ok);
|
|
||||||
response.reason("OK");
|
|
||||||
response.set(http::field::server, "C++ Server");
|
|
||||||
response.set(http::field::content_type, "text/html");
|
|
||||||
response.body() = msg;
|
|
||||||
response.prepare_payload();
|
|
||||||
|
|
||||||
// Send the response to the client
|
|
||||||
http::write(socket, response);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
try {
|
|
||||||
net::io_context io_context{BOOST_ASIO_CONCURRENCY_HINT_UNSAFE_IO};
|
|
||||||
|
|
||||||
// Create an endpoint to bind to
|
|
||||||
tcp::endpoint endpoint(tcp::v4(), 8070);
|
|
||||||
|
|
||||||
// Create and bind the acceptor
|
|
||||||
tcp::acceptor acceptor(io_context, endpoint);
|
|
||||||
std::cout << "Server listening on port 8070..." << std::endl;
|
|
||||||
|
|
||||||
// static 17-byte string
|
|
||||||
std::string msg = "Hello from C++!!!";
|
|
||||||
// or
|
|
||||||
// Read HTML content from a file (e.g., "index.html")
|
|
||||||
// std::string html_content = read_html_file("hello.html");
|
|
||||||
|
|
||||||
// std::cout << "str len: " << (html_content.length() == msg.length()) << std::boolalpha << "\n";
|
|
||||||
|
|
||||||
// Create a thread pool with 4 threads
|
|
||||||
net::thread_pool pool(4);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
// Wait for a client to connect
|
|
||||||
tcp::socket socket(io_context);
|
|
||||||
acceptor.accept(socket);
|
|
||||||
|
|
||||||
// Post a task to the thread pool to handle the client request
|
|
||||||
net::post(pool, [socket = std::move(socket), msg]() mutable {
|
|
||||||
handle_client(std::move(socket), msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// The thread pool destructor will ensure that all threads are joined properly.
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
|
||||||
builder.Logging.ClearProviders();
|
|
||||||
|
|
||||||
var app = builder.Build();
|
|
||||||
|
|
||||||
app.MapGet("/", () => "Hello from C#");
|
|
||||||
|
|
||||||
app.Run();
|
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "http://localhost:5026",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<PublishAot>true</PublishAot>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
|
@ -1,16 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func hello(w http.ResponseWriter, req *http.Request) {
|
|
||||||
fmt.Fprintf(w, "hello from GO!!!\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
print("listening on 0.0.0.0:8090\n")
|
|
||||||
http.HandleFunc("/hello", hello)
|
|
||||||
http.ListenAndServe(":8090", nil)
|
|
||||||
}
|
|
90
wrk/graph.py
|
@ -1,90 +0,0 @@
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from matplotlib.ticker import FuncFormatter
|
|
||||||
from collections import defaultdict
|
|
||||||
import statistics
|
|
||||||
|
|
||||||
directory = "./wrk" # Replace with the actual directory path
|
|
||||||
|
|
||||||
requests_sec = defaultdict(list)
|
|
||||||
transfers_sec = defaultdict(list)
|
|
||||||
|
|
||||||
mean_requests = {}
|
|
||||||
mean_transfers = {}
|
|
||||||
|
|
||||||
|
|
||||||
def plot(kind='', title='', ylabel='', means=None):
|
|
||||||
# Sort the labels and requests_sec lists together based on the requests_sec values
|
|
||||||
labels = []
|
|
||||||
values = []
|
|
||||||
|
|
||||||
# silly, I know
|
|
||||||
for k, v in means.items():
|
|
||||||
labels.append(k)
|
|
||||||
values.append(v)
|
|
||||||
|
|
||||||
# sort the labels and value lists
|
|
||||||
labels, values = zip(*sorted(zip(labels, values), key=lambda x: x[1], reverse=True))
|
|
||||||
|
|
||||||
# Plot the graph
|
|
||||||
plt.figure(figsize=(10, 6)) # Adjust the figure size as needed
|
|
||||||
bars = plt.bar(labels, values)
|
|
||||||
plt.xlabel("Subject")
|
|
||||||
plt.ylabel(ylabel)
|
|
||||||
plt.title(title)
|
|
||||||
plt.xticks(rotation=45) # Rotate x-axis labels for better readability
|
|
||||||
|
|
||||||
# Display the actual values on top of the bars
|
|
||||||
for bar in bars:
|
|
||||||
yval = bar.get_height()
|
|
||||||
plt.text(bar.get_x() + bar.get_width() / 2, yval, f'{yval:,.2f}', ha='center', va='bottom')
|
|
||||||
|
|
||||||
plt.tight_layout() # Adjust the spacing of the graph elements
|
|
||||||
png_name = f"{directory}/{kind.lower()}_graph.png"
|
|
||||||
plt.savefig(png_name) # Save the graph as a PNG file
|
|
||||||
print(f"Generated: {png_name}")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
if not os.path.isdir(".git"):
|
|
||||||
print("Please run from root directory of the repository!")
|
|
||||||
print("e.g. python wrk/graph.py")
|
|
||||||
import sys
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Iterate over the files in the directory
|
|
||||||
for filename in os.listdir(directory):
|
|
||||||
if filename.endswith(".perflog"):
|
|
||||||
label = os.path.splitext(filename)[0]
|
|
||||||
file_path = os.path.join(directory, filename)
|
|
||||||
|
|
||||||
with open(file_path, "r") as file:
|
|
||||||
lines = file.readlines()
|
|
||||||
for line in lines:
|
|
||||||
# Extract the Requests/sec value using regular expressions
|
|
||||||
match = re.search(r"Requests/sec:\s+([\d.]+)", line)
|
|
||||||
if match:
|
|
||||||
requests_sec[label].append(float(match.group(1)))
|
|
||||||
match = re.search(r"Transfer/sec:\s+([\d.]+)", line)
|
|
||||||
if match:
|
|
||||||
value = float(match.group(1))
|
|
||||||
if 'KB' in line:
|
|
||||||
value *= 1024
|
|
||||||
elif 'MB' in line:
|
|
||||||
value *= 1024 * 1024
|
|
||||||
value /= 1024.0 * 1024
|
|
||||||
transfers_sec[label].append(value)
|
|
||||||
|
|
||||||
# calculate means
|
|
||||||
for k, v in requests_sec.items():
|
|
||||||
mean_requests[k] = statistics.mean(v)
|
|
||||||
|
|
||||||
for k, v in transfers_sec.items():
|
|
||||||
mean_transfers[k] = statistics.mean(v)
|
|
||||||
|
|
||||||
# save the plots
|
|
||||||
plot(kind='req_per_sec', title='Requests/sec Comparison',
|
|
||||||
ylabel='requests/sec', means=mean_requests)
|
|
||||||
plot(kind='xfer_per_sec', title='Transfer/sec Comparison',
|
|
||||||
ylabel='transfer/sec [MB]', means=mean_transfers)
|
|
104
wrk/measure.sh
|
@ -1,104 +0,0 @@
|
||||||
#! /usr/bin/env bash
|
|
||||||
THREADS=4
|
|
||||||
CONNECTIONS=400
|
|
||||||
DURATION_SECONDS=10
|
|
||||||
|
|
||||||
SUBJECT=$1
|
|
||||||
|
|
||||||
if echo $(uname) -eq "Darwin" ; then
|
|
||||||
TSK_SRV=""
|
|
||||||
TSK_LOAD=""
|
|
||||||
else
|
|
||||||
TSK_SRV="taskset -c 0,1,2,3"
|
|
||||||
TSK_LOAD="taskset -c 4,5,6,7"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "" ] ; then
|
|
||||||
echo "usage: $0 subject # subject: zig or go"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "zig-zap" ] ; then
|
|
||||||
zig build -Doptimize=ReleaseFast wrk > /dev/null
|
|
||||||
$TSK_SRV ./zig-out/bin/wrk &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:3000
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "zigstd" ] ; then
|
|
||||||
zig build -Doptimize=ReleaseFast wrk_zigstd > /dev/null
|
|
||||||
$TSK_SRV ./zig-out/bin/wrk_zigstd &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:3000
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "go" ] ; then
|
|
||||||
cd wrk/go && go build main.go
|
|
||||||
$TSK_SRV ./main &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:8090/hello
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "python" ] ; then
|
|
||||||
$TSK_SRV python wrk/python/main.py &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:8080
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "python-sanic" ] ; then
|
|
||||||
$TSK_SRV python wrk/sanic/sanic-app.py &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:8000
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "rust-bythebook" ] ; then
|
|
||||||
cd wrk/rust/bythebook && cargo build --release
|
|
||||||
$TSK_SRV ./target/release/hello &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:7878
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "rust-bythebook-improved" ] ; then
|
|
||||||
cd wrk/rust/bythebook-improved && cargo build --release
|
|
||||||
$TSK_SRV ./target/release/hello &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:7878
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "rust-clean" ] ; then
|
|
||||||
cd wrk/rust/clean && cargo build --release
|
|
||||||
$TSK_SRV ./target/release/hello &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:7878
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "rust-axum" ] ; then
|
|
||||||
cd wrk/axum/hello-axum && cargo build --release
|
|
||||||
$TSK_SRV ./target/release/hello-axum &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:3000
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "csharp" ] ; then
|
|
||||||
cd wrk/csharp && dotnet publish csharp.csproj -o ./out
|
|
||||||
$TSK_SRV ./out/csharp --urls "http://127.0.0.1:5026" &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:5026
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SUBJECT" = "cpp-beast" ] ; then
|
|
||||||
cd wrk/cpp && zig build -Doptimize=ReleaseFast
|
|
||||||
$TSK_SRV ./zig-out/bin/cpp-beast 127.0.0.1 8070 . &
|
|
||||||
PID=$!
|
|
||||||
URL=http://127.0.0.1:8070
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
echo "========================================================================"
|
|
||||||
echo " $SUBJECT"
|
|
||||||
echo "========================================================================"
|
|
||||||
$TSK_LOAD wrk -c $CONNECTIONS -t $THREADS -d $DURATION_SECONDS --latency $URL
|
|
||||||
|
|
||||||
kill $PID
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
#! /usr/bin/env bash
|
|
||||||
|
|
||||||
if [ ! -d ".git" ] ; then
|
|
||||||
echo "This script must be run from the root directory of the repository!"
|
|
||||||
echo "./wrk/measure_all.sh"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
SUBJECTS="$1"
|
|
||||||
|
|
||||||
if [ "$SUBJECTS" = "README" ] ; then
|
|
||||||
rm -f wrk/*.perflog
|
|
||||||
SUBJECTS="zig-zap go python-sanic rust-axum csharp cpp-beast"
|
|
||||||
# above targets csharp and cpp-beast are out of date!
|
|
||||||
SUBJECTS="zig-zap go python-sanic rust-axum"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$SUBJECTS" ] ; then
|
|
||||||
SUBJECTS="zig-zap go python python-sanic rust-bythebook rust-bythebook-improved rust-clean rust-axum csharp cpp-beast"
|
|
||||||
# above targets csharp and cpp-beast are out of date!
|
|
||||||
SUBJECTS="zig-zap go python python-sanic rust-bythebook rust-bythebook-improved rust-clean rust-axum"
|
|
||||||
fi
|
|
||||||
|
|
||||||
for S in $SUBJECTS; do
|
|
||||||
L="$S.perflog"
|
|
||||||
rm -f wrk/$L
|
|
||||||
for R in 1 2 3 ; do
|
|
||||||
./wrk/measure.sh $S | tee -a wrk/$L
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Finished"
|
|
|
@ -1,52 +0,0 @@
|
||||||
# other measurements
|
|
||||||
|
|
||||||
## zap wrk 'example' with and without logging
|
|
||||||
|
|
||||||
**NO** performance regressions observable:
|
|
||||||
|
|
||||||
With `logging=true`:
|
|
||||||
|
|
||||||
```
|
|
||||||
[nix-shell:~/code/github.com/renerocksai/zap]$ ./wrk/measure.sh zig > out 2> /dev/null
|
|
||||||
|
|
||||||
[nix-shell:~/code/github.com/renerocksai/zap]$ cat out
|
|
||||||
========================================================================
|
|
||||||
zig
|
|
||||||
========================================================================
|
|
||||||
Running 10s test @ http://127.0.0.1:3000
|
|
||||||
4 threads and 400 connections
|
|
||||||
Thread Stats Avg Stdev Max +/- Stdev
|
|
||||||
Latency 343.91us 286.75us 18.37ms 95.58%
|
|
||||||
Req/Sec 162.61k 3.61k 174.96k 76.75%
|
|
||||||
Latency Distribution
|
|
||||||
50% 302.00us
|
|
||||||
75% 342.00us
|
|
||||||
90% 572.00us
|
|
||||||
99% 697.00us
|
|
||||||
6470789 requests in 10.01s, 0.96GB read
|
|
||||||
Requests/sec: 646459.59
|
|
||||||
Transfer/sec: 98.03MB
|
|
||||||
```
|
|
||||||
|
|
||||||
With `logging=false`:
|
|
||||||
|
|
||||||
```
|
|
||||||
[nix-shell:~/code/github.com/renerocksai/zap]$ ./wrk/measure.sh zig
|
|
||||||
Listening on 0.0.0.0:3000
|
|
||||||
========================================================================
|
|
||||||
zig
|
|
||||||
========================================================================
|
|
||||||
Running 10s test @ http://127.0.0.1:3000
|
|
||||||
4 threads and 400 connections
|
|
||||||
Thread Stats Avg Stdev Max +/- Stdev
|
|
||||||
Latency 336.10us 122.28us 14.67ms 88.55%
|
|
||||||
Req/Sec 159.82k 7.71k 176.75k 56.00%
|
|
||||||
Latency Distribution
|
|
||||||
50% 310.00us
|
|
||||||
75% 343.00us
|
|
||||||
90% 425.00us
|
|
||||||
99% 699.00us
|
|
||||||
6359415 requests in 10.01s, 0.94GB read
|
|
||||||
Requests/sec: 635186.96
|
|
||||||
Transfer/sec: 96.32MB
|
|
||||||
```
|
|
|
@ -1,32 +0,0 @@
|
||||||
# Python 3 server example
|
|
||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
||||||
|
|
||||||
hostName = "127.0.0.1"
|
|
||||||
serverPort = 8080
|
|
||||||
|
|
||||||
|
|
||||||
class MyServer(BaseHTTPRequestHandler):
|
|
||||||
def do_GET(self):
|
|
||||||
try:
|
|
||||||
self.send_response(200)
|
|
||||||
self.send_header("Content-type", "text/html")
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(bytes("HI FROM PYTHON!!!", "utf-8"))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
webServer = HTTPServer((hostName, serverPort), MyServer)
|
|
||||||
print("Server started http://%s:%s" % (hostName, serverPort))
|
|
||||||
|
|
||||||
try:
|
|
||||||
webServer.serve_forever()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
|
|
||||||
webServer.server_close()
|
|
||||||
print("Server stopped.")
|
|
14
wrk/rust/bythebook-improved/.gitignore
vendored
|
@ -1,14 +0,0 @@
|
||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
debug/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
||||||
*.pdb
|
|
|
@ -1,9 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "hello"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# crossbeam = { version = "0.8.2", features = ["crossbeam-channel"] }
|
|
|
@ -1,101 +0,0 @@
|
||||||
//Crossbeam should, but does not make this faster.
|
|
||||||
//use crossbeam::channel::bounded;
|
|
||||||
use std::{net::TcpStream, sync::mpsc, thread};
|
|
||||||
type Job = (fn(TcpStream), TcpStream);
|
|
||||||
|
|
||||||
type Sender = mpsc::Sender<Job>;
|
|
||||||
//type Sender = crossbeam::channel::Sender<Job>;
|
|
||||||
|
|
||||||
type Receiver = mpsc::Receiver<Job>;
|
|
||||||
//type Receiver = crossbeam::channel::Receiver<Job>;
|
|
||||||
|
|
||||||
pub struct ThreadPool {
|
|
||||||
workers: Vec<Worker>,
|
|
||||||
senders: Vec<Sender>,
|
|
||||||
|
|
||||||
next_sender: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThreadPool {
|
|
||||||
/// Create a new ThreadPool.
|
|
||||||
///
|
|
||||||
/// The size is the number of threads in the pool.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// The `new` function will panic if the size is zero.
|
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
|
||||||
assert!(size > 0);
|
|
||||||
|
|
||||||
let mut workers = Vec::with_capacity(size);
|
|
||||||
let mut senders = Vec::with_capacity(size);
|
|
||||||
|
|
||||||
for id in 0..size {
|
|
||||||
//let (sender, receiver) = bounded(2);
|
|
||||||
let (sender, receiver) = mpsc::channel();
|
|
||||||
senders.push(sender);
|
|
||||||
workers.push(Worker::new(id, receiver));
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadPool {
|
|
||||||
workers,
|
|
||||||
senders,
|
|
||||||
next_sender: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// round robin over available workers to ensure we never have to buffer requests
|
|
||||||
pub fn execute(&mut self, handler: fn(TcpStream), stream: TcpStream) {
|
|
||||||
let job = (handler, stream);
|
|
||||||
self.senders[self.next_sender].send(job).unwrap();
|
|
||||||
//self.senders[self.next_sender].try_send(job).unwrap();
|
|
||||||
self.next_sender += 1;
|
|
||||||
if self.next_sender == self.senders.len() {
|
|
||||||
self.next_sender = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ThreadPool {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.senders.clear();
|
|
||||||
|
|
||||||
for worker in &mut self.workers {
|
|
||||||
println!("Shutting down worker {}", worker.id);
|
|
||||||
if let Some(thread) = worker.thread.take() {
|
|
||||||
thread.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Worker {
|
|
||||||
id: usize,
|
|
||||||
thread: Option<thread::JoinHandle<()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Worker {
|
|
||||||
fn new(id: usize, receiver: Receiver) -> Worker {
|
|
||||||
let thread = thread::spawn(move || Self::work(receiver));
|
|
||||||
|
|
||||||
Worker {
|
|
||||||
id,
|
|
||||||
thread: Some(thread),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn work(receiver: Receiver) {
|
|
||||||
loop {
|
|
||||||
let message = receiver.recv();
|
|
||||||
match message {
|
|
||||||
Ok((handler, stream)) => {
|
|
||||||
// println!("Worker got a job; executing.");
|
|
||||||
handler(stream);
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// println!("Worker disconnected; shutting down.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
use hello::ThreadPool;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::net::TcpListener;
|
|
||||||
use std::net::TcpStream;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
|
|
||||||
//Creating a massive amount of threads so we can always have one ready to go.
|
|
||||||
let mut pool = ThreadPool::new(128);
|
|
||||||
|
|
||||||
for stream in listener.incoming() {
|
|
||||||
let stream = stream.unwrap();
|
|
||||||
//handle_connection(stream);
|
|
||||||
pool.execute(handle_connection, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Shutting down.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_connection(mut stream: TcpStream) {
|
|
||||||
stream.set_nodelay(true).expect("set_nodelay call failed");
|
|
||||||
loop{
|
|
||||||
let mut buffer = [0; 1024];
|
|
||||||
match stream.read(&mut buffer){
|
|
||||||
Err(_)=>return,
|
|
||||||
Ok(0)=>return,
|
|
||||||
Ok(_v)=>{},
|
|
||||||
}
|
|
||||||
|
|
||||||
let response_bytes = b"HTTP/1.1 200 OK\r\nContent-Length: 16\r\nConnection: keep-alive\r\n\r\nHELLO from RUST!";
|
|
||||||
|
|
||||||
stream.write_all(response_bytes).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
14
wrk/rust/bythebook/.gitignore
vendored
|
@ -1,14 +0,0 @@
|
||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
debug/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
||||||
*.pdb
|
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "hello"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
|
@ -1 +0,0 @@
|
||||||
Hello from RUST!
|
|
|
@ -1,92 +0,0 @@
|
||||||
use std::{
|
|
||||||
sync::{mpsc, Arc, Mutex},
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ThreadPool {
|
|
||||||
workers: Vec<Worker>,
|
|
||||||
sender: Option<mpsc::Sender<Job>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
type Job = Box<dyn FnOnce() + Send + 'static>;
|
|
||||||
|
|
||||||
impl ThreadPool {
|
|
||||||
/// Create a new ThreadPool.
|
|
||||||
///
|
|
||||||
/// The size is the number of threads in the pool.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// The `new` function will panic if the size is zero.
|
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
|
||||||
assert!(size > 0);
|
|
||||||
|
|
||||||
let (sender, receiver) = mpsc::channel();
|
|
||||||
|
|
||||||
let receiver = Arc::new(Mutex::new(receiver));
|
|
||||||
|
|
||||||
let mut workers = Vec::with_capacity(size);
|
|
||||||
|
|
||||||
for id in 0..size {
|
|
||||||
workers.push(Worker::new(id, Arc::clone(&receiver)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadPool {
|
|
||||||
workers,
|
|
||||||
sender: Some(sender),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute<F>(&self, f: F)
|
|
||||||
where
|
|
||||||
F: FnOnce() + Send + 'static,
|
|
||||||
{
|
|
||||||
let job = Box::new(f);
|
|
||||||
|
|
||||||
self.sender.as_ref().unwrap().send(job).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ThreadPool {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
drop(self.sender.take());
|
|
||||||
|
|
||||||
for worker in &mut self.workers {
|
|
||||||
println!("Shutting down worker {}", worker.id);
|
|
||||||
|
|
||||||
if let Some(thread) = worker.thread.take() {
|
|
||||||
thread.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Worker {
|
|
||||||
id: usize,
|
|
||||||
thread: Option<thread::JoinHandle<()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Worker {
|
|
||||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
|
||||||
let thread = thread::spawn(move || loop {
|
|
||||||
let message = receiver.lock().unwrap().recv();
|
|
||||||
|
|
||||||
match message {
|
|
||||||
Ok(job) => {
|
|
||||||
// println!("Worker got a job; executing.");
|
|
||||||
|
|
||||||
job();
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// println!("Worker disconnected; shutting down.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Worker {
|
|
||||||
id,
|
|
||||||
thread: Some(thread),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
use hello::ThreadPool;
|
|
||||||
// use std::fs;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::net::TcpListener;
|
|
||||||
use std::net::TcpStream;
|
|
||||||
// use std::thread;
|
|
||||||
// use std::time::Duration;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
|
|
||||||
let pool = ThreadPool::new(4);
|
|
||||||
|
|
||||||
// for stream in listener.incoming().take(2) {
|
|
||||||
for stream in listener.incoming() {
|
|
||||||
let stream = stream.unwrap();
|
|
||||||
|
|
||||||
pool.execute(|| {
|
|
||||||
handle_connection(stream);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Shutting down.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_connection(mut stream: TcpStream) {
|
|
||||||
let mut buffer = [0; 1024];
|
|
||||||
stream.read(&mut buffer).unwrap();
|
|
||||||
|
|
||||||
|
|
||||||
let status_line = "HTTP/1.1 200 OK";
|
|
||||||
|
|
||||||
let contents = "HELLO from RUST!";
|
|
||||||
|
|
||||||
let response = format!(
|
|
||||||
"{}\r\nContent-Length: {}\r\n\r\n{}",
|
|
||||||
status_line,
|
|
||||||
contents.len(),
|
|
||||||
contents
|
|
||||||
);
|
|
||||||
|
|
||||||
stream.write_all(response.as_bytes()).unwrap();
|
|
||||||
stream.flush().unwrap();
|
|
||||||
}
|
|
14
wrk/rust/clean/.gitignore
vendored
|
@ -1,14 +0,0 @@
|
||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
debug/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
||||||
*.pdb
|
|
|
@ -1,8 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "hello"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Hello from RUST!
|
|
|
@ -1,101 +0,0 @@
|
||||||
//Crossbeam should, but does not make this faster.
|
|
||||||
//use crossbeam::channel::bounded;
|
|
||||||
use std::{net::TcpStream, sync::mpsc, thread};
|
|
||||||
type Job = (fn(TcpStream), TcpStream);
|
|
||||||
|
|
||||||
type Sender = mpsc::Sender<Job>;
|
|
||||||
//type Sender = crossbeam::channel::Sender<Job>;
|
|
||||||
|
|
||||||
type Receiver = mpsc::Receiver<Job>;
|
|
||||||
//type Receiver = crossbeam::channel::Receiver<Job>;
|
|
||||||
|
|
||||||
pub struct ThreadPool {
|
|
||||||
workers: Vec<Worker>,
|
|
||||||
senders: Vec<Sender>,
|
|
||||||
|
|
||||||
next_sender: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThreadPool {
|
|
||||||
/// Create a new ThreadPool.
|
|
||||||
///
|
|
||||||
/// The size is the number of threads in the pool.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// The `new` function will panic if the size is zero.
|
|
||||||
pub fn new(size: usize) -> ThreadPool {
|
|
||||||
assert!(size > 0);
|
|
||||||
|
|
||||||
let mut workers = Vec::with_capacity(size);
|
|
||||||
let mut senders = Vec::with_capacity(size);
|
|
||||||
|
|
||||||
for id in 0..size {
|
|
||||||
//let (sender, receiver) = bounded(2);
|
|
||||||
let (sender, receiver) = mpsc::channel();
|
|
||||||
senders.push(sender);
|
|
||||||
workers.push(Worker::new(id, receiver));
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadPool {
|
|
||||||
workers,
|
|
||||||
senders,
|
|
||||||
next_sender: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// round robin over available workers to ensure we never have to buffer requests
|
|
||||||
pub fn execute(&mut self, handler: fn(TcpStream), stream: TcpStream) {
|
|
||||||
let job = (handler, stream);
|
|
||||||
self.senders[self.next_sender].send(job).unwrap();
|
|
||||||
//self.senders[self.next_sender].try_send(job).unwrap();
|
|
||||||
self.next_sender += 1;
|
|
||||||
if self.next_sender == self.senders.len() {
|
|
||||||
self.next_sender = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ThreadPool {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.senders.clear();
|
|
||||||
|
|
||||||
for worker in &mut self.workers {
|
|
||||||
println!("Shutting down worker {}", worker.id);
|
|
||||||
if let Some(thread) = worker.thread.take() {
|
|
||||||
thread.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Worker {
|
|
||||||
id: usize,
|
|
||||||
thread: Option<thread::JoinHandle<()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Worker {
|
|
||||||
fn new(id: usize, receiver: Receiver) -> Worker {
|
|
||||||
let thread = thread::spawn(move || Self::work(receiver));
|
|
||||||
|
|
||||||
Worker {
|
|
||||||
id,
|
|
||||||
thread: Some(thread),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn work(receiver: Receiver) {
|
|
||||||
loop {
|
|
||||||
let message = receiver.recv();
|
|
||||||
match message {
|
|
||||||
Ok((handler, stream)) => {
|
|
||||||
// println!("Worker got a job; executing.");
|
|
||||||
handler(stream);
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// println!("Worker disconnected; shutting down.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::net::TcpListener;
|
|
||||||
use std::net::TcpStream;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
|
|
||||||
|
|
||||||
for stream in listener.incoming() {
|
|
||||||
let stream = stream.unwrap();
|
|
||||||
//handle_connection(stream);
|
|
||||||
std::thread::spawn(||{handle_connection(stream)});
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Shutting down.");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_connection(mut stream: TcpStream) {
|
|
||||||
stream.set_nodelay(true).expect("set_nodelay call failed");
|
|
||||||
loop{
|
|
||||||
let mut buffer = [0; 1024];
|
|
||||||
match stream.read(&mut buffer){
|
|
||||||
Err(_)=>return,
|
|
||||||
Ok(0)=>return,
|
|
||||||
Ok(_v)=>{},
|
|
||||||
}
|
|
||||||
|
|
||||||
let response_bytes = b"HTTP/1.1 200 OK\r\nContent-Length: 16\r\nConnection: keep-alive\r\n\r\nHELLO from RUST!";
|
|
||||||
|
|
||||||
stream.write_all(response_bytes).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 46 KiB |
|
@ -1,12 +0,0 @@
|
||||||
from sanic import Sanic
|
|
||||||
from sanic.response import html
|
|
||||||
|
|
||||||
app = Sanic("sanic-app")
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
async def test(request):
|
|
||||||
return html("Hello from sanic!", 200)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run()
|
|
|
@ -1,24 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const zap = @import("zap");
|
|
||||||
|
|
||||||
fn on_request_minimal(r: zap.Request) !void {
|
|
||||||
try r.sendBody("Hello from ZAP!!!");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
var listener = zap.HttpListener.init(.{
|
|
||||||
.port = 3000,
|
|
||||||
.on_request = on_request_minimal,
|
|
||||||
.log = false,
|
|
||||||
.max_clients = 100000,
|
|
||||||
});
|
|
||||||
try listener.listen();
|
|
||||||
|
|
||||||
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
|
|
||||||
|
|
||||||
// start worker threads
|
|
||||||
zap.start(.{
|
|
||||||
.threads = 4,
|
|
||||||
.workers = 4, // empirical tests: yield best perf on my machine
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
// var gpa = std.heap.GeneralPurposeAllocator(.{
|
|
||||||
// .thread_safe = true,
|
|
||||||
// }){};
|
|
||||||
// const allocator = gpa.allocator();
|
|
||||||
|
|
||||||
const address = try std.net.Address.parseIp("127.0.0.1", 3000);
|
|
||||||
var http_server = try address.listen(.{
|
|
||||||
.reuse_address = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
var read_buffer: [2048]u8 = undefined;
|
|
||||||
|
|
||||||
// const max_header_size = 8192;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const connection = try http_server.accept();
|
|
||||||
defer connection.stream.close();
|
|
||||||
var server = std.http.Server.init(connection, &read_buffer);
|
|
||||||
|
|
||||||
var request = try server.receiveHead();
|
|
||||||
const server_body: []const u8 = "HI FROM ZIG STD!\n";
|
|
||||||
|
|
||||||
try request.respond(server_body, .{
|
|
||||||
.extra_headers = &.{
|
|
||||||
.{ .name = "content_type", .value = "text/plain" },
|
|
||||||
.{ .name = "connection", .value = "close" },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|