Skip to content

use NIOSingletons EventLoops/NIOThreadPool instead of spawning new #697

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

Merged
merged 1 commit into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ let package = Package(
.library(name: "AsyncHTTPClient", targets: ["AsyncHTTPClient"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.50.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.58.0"),
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.22.0"),
.package(url: "https://github.com/apple/swift-nio-http2.git", from: "1.19.0"),
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.13.0"),
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.11.4"),
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.19.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.4.4"),
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"),
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
Expand Down
27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,10 @@ and `AsyncHTTPClient` dependency to your target:

The code snippet below illustrates how to make a simple GET request to a remote server.

Please note that the example will spawn a new `EventLoopGroup` which will _create fresh threads_ which is a very costly operation. In a real-world application that uses [SwiftNIO](https://github.com/apple/swift-nio) for other parts of your application (for example a web server), please prefer `eventLoopGroupProvider: .shared(myExistingEventLoopGroup)` to share the `EventLoopGroup` used by AsyncHTTPClient with other parts of your application.

If your application does not use SwiftNIO yet, it is acceptable to use `eventLoopGroupProvider: .createNew` but please make sure to share the returned `HTTPClient` instance throughout your whole application. Do not create a large number of `HTTPClient` instances with `eventLoopGroupProvider: .createNew`, this is very wasteful and might exhaust the resources of your program.

```swift
import AsyncHTTPClient

let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)

/// MARK: - Using Swift Concurrency
let request = HTTPClientRequest(url: "https://apple.com/")
Expand Down Expand Up @@ -78,7 +74,7 @@ The default HTTP Method is `GET`. In case you need to have more control over the
```swift
import AsyncHTTPClient

let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
do {
var request = HTTPClientRequest(url: "https://apple.com/")
request.method = .POST
Expand All @@ -103,9 +99,10 @@ try await httpClient.shutdown()
```swift
import AsyncHTTPClient

let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
try? httpClient.syncShutdown()
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

var request = try HTTPClient.Request(url: "https://apple.com/", method: .POST)
Expand All @@ -129,15 +126,15 @@ httpClient.execute(request: request).whenComplete { result in
### Redirects following
Enable follow-redirects behavior using the client configuration:
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew,
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton,
configuration: HTTPClient.Configuration(followRedirects: true))
```

### Timeouts
Timeouts (connect and read) can also be set using the client configuration:
```swift
let timeout = HTTPClient.Configuration.Timeout(connect: .seconds(1), read: .seconds(1))
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew,
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton,
configuration: HTTPClient.Configuration(timeout: timeout))
```
or on a per-request basis:
Expand All @@ -151,7 +148,7 @@ The following example demonstrates how to count the number of bytes in a streami

#### Using Swift Concurrency
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
do {
let request = HTTPClientRequest(url: "https://apple.com/")
let response = try await httpClient.execute(request, timeout: .seconds(30))
Expand Down Expand Up @@ -251,7 +248,7 @@ asynchronously, while reporting the download progress at the same time, like in
example:

```swift
let client = HTTPClient(eventLoopGroupProvider: .createNew)
let client = HTTPClient(eventLoopGroupProvider: .singleton)
let request = try HTTPClient.Request(
url: "https://swift.org/builds/development/ubuntu1804/latest-build.yml"
)
Expand All @@ -275,7 +272,7 @@ client.execute(request: request, delegate: delegate).futureResult
### Unix Domain Socket Paths
Connecting to servers bound to socket paths is easy:
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
httpClient.execute(
.GET,
socketPath: "/tmp/myServer.socket",
Expand All @@ -285,7 +282,7 @@ httpClient.execute(

Connecting over TLS to a unix domain socket path is possible as well:
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
httpClient.execute(
.POST,
secureSocketPath: "/tmp/myServer.socket",
Expand All @@ -312,7 +309,7 @@ The exclusive use of HTTP/1 is possible by setting `httpVersion` to `.http1Only`
var configuration = HTTPClient.Configuration()
configuration.httpVersion = .http1Only
let client = HTTPClient(
eventLoopGroupProvider: .createNew,
eventLoopGroupProvider: .singleton,
configuration: configuration
)
```
Expand Down
56 changes: 41 additions & 15 deletions Sources/AsyncHTTPClient/Docs.docc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ and `AsyncHTTPClient` dependency to your target:

The code snippet below illustrates how to make a simple GET request to a remote server.

Please note that the example will spawn a new `EventLoopGroup` which will _create fresh threads_ which is a very costly operation. In a real-world application that uses [SwiftNIO](https://github.com/apple/swift-nio) for other parts of your application (for example a web server), please prefer `eventLoopGroupProvider: .shared(myExistingEventLoopGroup)` to share the `EventLoopGroup` used by AsyncHTTPClient with other parts of your application.

If your application does not use SwiftNIO yet, it is acceptable to use `eventLoopGroupProvider: .createNew` but please make sure to share the returned `HTTPClient` instance throughout your whole application. Do not create a large number of `HTTPClient` instances with `eventLoopGroupProvider: .createNew`, this is very wasteful and might exhaust the resources of your program.

```swift
import AsyncHTTPClient

let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason these examples don't use the zero-element initializer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Lukasa Yes, that's only coming in #705 . #705 does also rewrite all these examples to just use HTTPClient.shared. But I split this work into two parts (maybe misguidedly):

  1. Introduce the .singleton EventLoopGroupProvider --> this PR
  2. Introduce HTTPClient.shared, a completely shared singleton HTTPClient that doesn't need shutdown --> #700 & #701: HTTPClient.shared a globally shared singleton #705

defer {
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

/// MARK: - Using Swift Concurrency
let request = HTTPClientRequest(url: "https://apple.com/")
Expand Down Expand Up @@ -82,7 +82,12 @@ The default HTTP Method is `GET`. In case you need to have more control over the
```swift
import AsyncHTTPClient

let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

do {
var request = HTTPClientRequest(url: "https://apple.com/")
request.method = .POST
Expand All @@ -107,9 +112,10 @@ try await httpClient.shutdown()
```swift
import AsyncHTTPClient

let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
try? httpClient.syncShutdown()
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

var request = try HTTPClient.Request(url: "https://apple.com/", method: .POST)
Expand All @@ -133,15 +139,15 @@ httpClient.execute(request: request).whenComplete { result in
#### Redirects following
Enable follow-redirects behavior using the client configuration:
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew,
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton,
configuration: HTTPClient.Configuration(followRedirects: true))
```

#### Timeouts
Timeouts (connect and read) can also be set using the client configuration:
```swift
let timeout = HTTPClient.Configuration.Timeout(connect: .seconds(1), read: .seconds(1))
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew,
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton,
configuration: HTTPClient.Configuration(timeout: timeout))
```
or on a per-request basis:
Expand All @@ -155,7 +161,12 @@ The following example demonstrates how to count the number of bytes in a streami

##### Using Swift Concurrency
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

do {
let request = HTTPClientRequest(url: "https://apple.com/")
let response = try await httpClient.execute(request, timeout: .seconds(30))
Expand Down Expand Up @@ -255,7 +266,12 @@ asynchronously, while reporting the download progress at the same time, like in
example:

```swift
let client = HTTPClient(eventLoopGroupProvider: .createNew)
let client = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

let request = try HTTPClient.Request(
url: "https://swift.org/builds/development/ubuntu1804/latest-build.yml"
)
Expand All @@ -279,7 +295,12 @@ client.execute(request: request, delegate: delegate).futureResult
#### Unix Domain Socket Paths
Connecting to servers bound to socket paths is easy:
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

httpClient.execute(
.GET,
socketPath: "/tmp/myServer.socket",
Expand All @@ -289,7 +310,12 @@ httpClient.execute(

Connecting over TLS to a unix domain socket path is possible as well:
```swift
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
let httpClient = HTTPClient(eventLoopGroupProvider: .singleton)
defer {
// Shutdown is guaranteed to work if it's done precisely once (which is the case here).
try! httpClient.syncShutdown()
}

httpClient.execute(
.POST,
secureSocketPath: "/tmp/myServer.socket",
Expand All @@ -316,7 +342,7 @@ The exclusive use of HTTP/1 is possible by setting ``HTTPClient/Configuration/ht
var configuration = HTTPClient.Configuration()
configuration.httpVersion = .http1Only
let client = HTTPClient(
eventLoopGroupProvider: .createNew,
eventLoopGroupProvider: .singleton,
configuration: configuration
)
```
Expand Down
Loading