Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make a GracefulShutdown helper in hyper-util #2862

Open
seanmonstar opened this issue May 20, 2022 · 7 comments · May be fixed by hyperium/hyper-util#108
Open

Make a GracefulShutdown helper in hyper-util #2862

seanmonstar opened this issue May 20, 2022 · 7 comments · May be fixed by hyperium/hyper-util#108
Labels
A-server Area: server. C-feature Category: feature. This is adding a new feature. E-medium Effort: medium. Some knowledge of how hyper internal works would be useful. K-hyper-util Crate: hyper-util

Comments

@seanmonstar
Copy link
Member

seanmonstar commented May 20, 2022

With the removal of hyper::Server, the graceful shutdown stuff would disappear. We want to continue to provide a helper to do that, in hyper-util, but make it also able to be used with timeouts. Perhaps something like this:

let listener = TcpListener::bind("0.0.0.0:0").await?;

let server = Server::new();

let graceful = GracefulShutdown::new();
let ctrl_c = tokio::signal::ctrl_c();

loop {
    tokio::select! {
        conn = listener.accept() => {
            let server = server.clone();
            let graceful = graceful.clone();
            task::spawn(async move {
                // maybe TLS accept here...

                let conn = server.serve_connection(conn);
                let conn = graceful.watch(conn);

                if let Err(e) = conn.await {
                    eprintln!("http server conn error: {}", e);
                }
            });
        },

        _ = ctrl_c => {
            eprintln!("Ctrl-C received, starting shutdown");
                break;
        }
    }
}

tokio::select! {
    _ = graceful.shutdown() => {
        eprintln!("Gracefully shutdown!");
    },
    _ = tokio::time::sleep(10.seconds()) => {
        eprintln!("Waited 10 seconds for graceful shutdown, aborting...");
    }
}
@seanmonstar seanmonstar added A-server Area: server. C-feature Category: feature. This is adding a new feature. E-medium Effort: medium. Some knowledge of how hyper internal works would be useful. K-hyper-util Crate: hyper-util labels May 20, 2022
@seanmonstar seanmonstar added this to the 1.0 milestone May 20, 2022
@jacob-pro
Copy link

jacob-pro commented Aug 12, 2022

Hi @seanmonstar

I previously had a go at writing an implementation of this here:
https://github.com/jacob-pro/hyper-graceful/blob/master/examples/example.rs

One issue I struggled with doing this in a seperate crate is that I need to call UpgradeableConnection::graceful_shutdown when UpgradeableConnection is private - I wonder if graceful_shutdown needs to be a public trait instead?

For now I worked around it by using a closure so the user has to call graceful_shutdown directly themselves.

My other suggestion / question was about building a more general purpose "Connection Manager" which could have features similar to http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html ?

@jfreedman0212
Copy link

Hey! Not sure if this is still an outstanding issue, but I can take a stab at it. I've implemented something like this with tokio's JoinSet before (docs). Would that be interesting?

I can whip up a little code example later, not at my computer right now.

@seanmonstar
Copy link
Member Author

It is still outstanding :)

I admit I'm less familiar with JoinSet, what would it bring to the table? It would probably be nice if this util didn't need to be tied to Tokio. I think the usage pattern shown in the issue description is probably still a good thing to aim for, which allows people to spawn the tasks in whatever thing they want, after wrapping the connection in some watcher. I'd imagine the watcher itself is just an impl Future.

@jfreedman0212
Copy link

I was thinking that it would be easier to track the completion of the tasks instead of the connections themselves, which JoinSet is suited for. However, I didn't realize the connections themselves have graceful_shutdown functions (even though it's mentioned in this thread 😅), so JoinSet doesn't add anything that isn't already there.

I agree with you on keeping it runtime-agnostic and using the usage pattern you mentioned. To that end, I think the example @jacob-pro provided is a step in the right direction. The issue with it is that connections aren't shutdown by the ConnectionManager, they are instead shutdown by a closure the caller provides. Like they said, that could be fixed by having hyper expose a trait that wraps around the graceful_shutdown function on both http1::Connection and http2::Connection.

Alternatively, maybe having a GracefulShutdown implementation for both HTTP1 and HTTP2 separately could work? I've played around with it a bit, but I'm having a hard time getting the types to check out for passing an http1::Connection as itself (and not just a generic Future like in Jacob's example).

Are either of these options going down the right path? I feel like I'm spinning my wheels trying to get something to work following the usage you laid out. I'm probably just missing something

@seanmonstar seanmonstar removed this from the hyper-util 0.1 milestone Nov 15, 2023
wfly1998 added a commit to cloudwego/volo that referenced this issue Nov 24, 2023
Note that `hyper` v1.0.0-rc4 replaces IO traits from `tokio` with its
IO traits in `hyper::rt`.  To solve the building problem, we can
introduce `TokioIo` from `hyper-util` and wrap the `DefaultIncoming`
by the `TokioIo`.

Also, the `volo-grpc` uses `hyper` v0.14 with its auto-version
(using both http1 and http2) and graceful shutdown, but the latest
`hyper` and `hyper-util` cannot use the both features at the same
time, so the version of `hyper` in `volo` has not been upgraded.

Ref:
- hyperium/hyper@f9f65b7
- hyperium/hyper#3013
- hyperium/hyper#2862

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
wfly1998 added a commit to cloudwego/volo that referenced this issue Nov 27, 2023
Note that `hyper` v1.0.0-rc4 replaces IO traits from `tokio` with its
IO traits in `hyper::rt`.  To solve the building problem, we can
introduce `TokioIo` from `hyper-util` and wrap the `DefaultIncoming`
by the `TokioIo`.

Also, the `volo-grpc` uses `hyper` v0.14 with its auto-version
(using both http1 and http2) and graceful shutdown, but the latest
`hyper` and `hyper-util` cannot use the both features at the same
time, so the version of `hyper` in `volo` has not been upgraded.

Ref:
- hyperium/hyper@f9f65b7
- hyperium/hyper#3013
- hyperium/hyper#2862

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
wfly1998 added a commit to cloudwego/volo that referenced this issue Nov 27, 2023
Note that `hyper` v1.0.0-rc4 replaces IO traits from `tokio` with its
IO traits in `hyper::rt`.  To solve the building problem, we can
introduce `TokioIo` from `hyper-util` and wrap the `DefaultIncoming`
by the `TokioIo`.

Also, the `volo-grpc` uses `hyper` v0.14 with its auto-version
(using both http1 and http2) and graceful shutdown, but the latest
`hyper` and `hyper-util` cannot use the both features at the same
time, so the version of `hyper` in `volo` has not been upgraded.

Ref:
- hyperium/hyper@f9f65b7
- hyperium/hyper#3013
- hyperium/hyper#2862

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
@GlenDC
Copy link
Contributor

GlenDC commented Nov 27, 2023

@seanmonstar can I pick this up together with the issue to add a Server to hyper-util?

#3436

@seanmonstar
Copy link
Member Author

I think the two items are probably worth being separate. The Server one has some more design to work out.

wfly1998 added a commit to cloudwego/volo that referenced this issue Nov 28, 2023
* feat: introduce volo-http crate

* feat(volo-http): introduce route id

> The `matchit::Router` cannot be converted to `Iterator`, so using
> `matchit::Router<DynService>` is not convenient enough.

> To solve the problem, we refer to the implementation of `axum` and
> introduce a `RouteId` as a bridge, the `matchit::Router` only handles
> some IDs and each ID corresponds to a `DynService`.

* feat(volo-http): wrap `DynService` with `Route`

> With tie type `Route`, it can support `Service`, `Handler` and so on.

* feat(volo-http): make handler more powerful

> With this commit, there is no need to keep `HttpContext` in handler
> function.  Like axum, anything with `FromContext` or `FromRequest`
> trait can be used as arguments of a handler.

* chore(volo-http): bump to hyper 1.0.0

> Note that `hyper` v1.0.0-rc4 replaces IO traits from `tokio` with its
> IO traits in `hyper::rt`.  To solve the building problem, we can
> introduce `TokioIo` from `hyper-util` and wrap the `DefaultIncoming`
> by the `TokioIo`.
> 
> Also, the `volo-grpc` uses `hyper` v0.14 with its auto-version
> (using both http1 and http2) and graceful shutdown, but the latest
> `hyper` and `hyper-util` cannot use the both features at the same
> time, so the version of `hyper` in `volo` has not been upgraded.
> 
> Ref:
> - hyperium/hyper@f9f65b7
> - hyperium/hyper#3013
> - hyperium/hyper#2862

* feat(volo-http): support `with_state`

* fix(volo-http): fix timeout problem of graceful shutdown

> With the previous codes, server could not stop immediately after
> pressing Ctrl+C, but waited until timeout before exiting.
> 
> This commit uses the same approach as "volo-thrift" and the problem
> has been resolved.

* feat(volo-http): support fallback

> This commit supports fallback in `Router` and `MethodRouter`. With
> `fallback`, when no route or method can handle the request, the
> `fallback` will be called and the specified response will be returned.

* feat(volo-http): add `TimeoutLayer` with response

> This commit adds a `TimeoutLayer` for `volo-http` which can use a
> function as a callback and when timeout occurs, it will return a
> response by the callback.

* chore(volo-http): bump version to `0.1.0`

* credits: update license of axum

---------

Signed-off-by: Gwo Tzu-Hsing <gotzehsing@gmail.com>
Signed-off-by: Zheng Li <875543533@qq.com>
Signed-off-by: bobozhengsir <bobozhengsir@gmail.com>
Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
@GlenDC GlenDC linked a pull request Mar 13, 2024 that will close this issue
@GlenDC
Copy link
Contributor

GlenDC commented Mar 13, 2024

Sorry @seanmonstar and @dswij for the confusion. Deleted the repo attached to the original PR not realising the PR was attached to it. It is now recreated as-is in hyperium/hyper-util#108

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-server Area: server. C-feature Category: feature. This is adding a new feature. E-medium Effort: medium. Some knowledge of how hyper internal works would be useful. K-hyper-util Crate: hyper-util
Projects
No open projects
Status: Todo
4 participants