Skip to content

Commit 43524b8

Browse files
committed
Better documentation.
1 parent fb32a4d commit 43524b8

File tree

3 files changed

+121
-73
lines changed

3 files changed

+121
-73
lines changed

guides/getting-started/readme.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Getting Started
2+
3+
This guide explains how to use use `Async::HTTP::Faraday` as a drop-in replacement for improved concurrency.
4+
5+
## Installation
6+
7+
Add the gem to your project:
8+
9+
~~~ bash
10+
$ bundle add async-http-faraday
11+
~~~
12+
13+
## Usage
14+
15+
The simplest way to use `Async::HTTP::Faraday` is to set it as the default adapter for Faraday. This will make all requests asynchronous.
16+
17+
~~~ ruby
18+
require 'async/http/faraday/default'
19+
~~~
20+
21+
This will configure `Faraday.default_adapter`.
22+
23+
### Custom Connection
24+
25+
You can configure a custom connection to use the async adapter:
26+
27+
``` ruby
28+
# Per connection:
29+
connection = Faraday.new(...) do |builder|
30+
builder.adapter :async_http
31+
end
32+
```
33+
34+
Here is how you make a request:
35+
36+
``` ruby
37+
response = connection.get("/index")
38+
```
39+
40+
### Thread Safety
41+
42+
By default, the faraday adapter uses a per-thread persistent client cache. This is safe to use in multi-threaded environments, in other words, if you have a single global faraday connection, and use that everywhere, it will be thread-safe. However, a consequence of that is you may experience elevated memory usage if you have many threads, as each thread will have its own connection pool. This is a desirable share-nothing architecture which helps to isolate problems, but if you don't use a multi-threaded environment, you may want to avoid the overhead. You can do this by configuring the `clients` option:
43+
44+
~~~ruby
45+
connection = Faraday.new(...) do |builder|
46+
# The default `clients:` is `Async::HTTP::Faraday::PerThreadPersistentClients`.
47+
builder.adapter :async_http, clients: Async::HTTP::Faraday::PersistentClients
48+
end
49+
~~~
50+
51+
The value of isolation cannot be overstated - if you can design you program using a share-nothing (between threads) architecture, you will have a much easier time debugging and reasoning about your program, however this comes at the cost of increased resource usage.
52+
53+
Alternatively, if you do not want to cache client connections, you can use the `Async::HTTP::Faraday::Clients` interface, which closes the connection after each request:
54+
55+
~~~ruby
56+
connection = Faraday.new(...) do |builder|
57+
builder.adapter :async_http, clients: Async::HTTP::Faraday::Clients
58+
end
59+
~~~
60+
61+
This will reduce memory usage but increase the latency of every request.

lib/async/http/faraday/clients.rb

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,23 @@
1919
module Async
2020
module HTTP
2121
module Faraday
22+
# An interface for creating and managing HTTP clients.
2223
class Clients
24+
# Create a new instance of the class.
2325
def self.call(...)
2426
new(...)
2527
end
2628

29+
# Create a new interface for managing HTTP clients.
30+
#
31+
# @parameter options [Hash] The options to create the clients with.
32+
# @parameter block [Proc] An optional block to call with the client before it is used.
2733
def initialize(**options, &block)
2834
@options = options
2935
@block = block
3036
end
3137

38+
# Close all clients.
3239
def close
3340
end
3441

@@ -41,9 +48,10 @@ def make_client(endpoint)
4148
return client
4249
end
4350

44-
# Get a client for the given endpoint. If a client already exists for the host, it will be reused.
51+
# Get a client for the given endpoint.
4552
#
4653
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
54+
# @yields {|client| ...} A client for the given endpoint.
4755
def with_client(endpoint)
4856
client = make_client(endpoint)
4957

@@ -52,10 +60,11 @@ def with_client(endpoint)
5260
client&.close
5361
end
5462

55-
# Get a client for the given proxy endpoint and endpoint. If a client already exists for the host, it will be reused.
63+
# Get a client for the given proxy endpoint and endpoint.
5664
#
5765
# @parameter proxy_endpoint [IO::Endpoint::Generic] The proxy endpoint to use.
5866
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
67+
# @yields {|client| ...} A client for the given endpoint.
5968
def with_proxied_client(proxy_endpoint, endpoint)
6069
client = client_for(proxy_endpoint)
6170
proxied_client = client.proxied_client(endpoint)
@@ -67,13 +76,16 @@ def with_proxied_client(proxy_endpoint, endpoint)
6776
end
6877
end
6978

79+
# An interface for creating and managing persistent HTTP clients.
7080
class PersistentClients < Clients
81+
# Create a new instance of the class.
7182
def initialize(...)
7283
super
7384

7485
@clients = {}
7586
end
7687

88+
# Close all clients.
7789
def close
7890
super
7991

@@ -83,32 +95,9 @@ def close
8395
clients.each(&:close)
8496
end
8597

86-
# Get the host key for the given endpoint.
87-
#
88-
# This is used to cache clients for the same host.
89-
#
90-
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the host key for.
91-
def host_key(endpoint)
92-
url = endpoint.url.dup
93-
94-
url.path = ""
95-
url.fragment = nil
96-
url.query = nil
97-
98-
return url
99-
end
100-
10198
# Get a client for the given endpoint. If a client already exists for the host, it will be reused.
10299
#
103-
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
104-
def client_for(endpoint)
105-
key = host_key(endpoint)
106-
107-
fetch(key) do
108-
make_client
109-
end
110-
end
111-
100+
# @yields {|client| ...} A client for the given endpoint.
112101
def with_client(endpoint)
113102
yield make_client(endpoint)
114103
end
@@ -127,31 +116,68 @@ def with_proxied_client(proxy_endpoint, endpoint)
127116
yield proxied_client
128117
end
129118

130-
protected
119+
private
131120

132121
def fetch(key)
133122
@clients.fetch(key) do
134123
@clients[key] = yield
135124
end
136125
end
126+
127+
def host_key(endpoint)
128+
url = endpoint.url.dup
129+
130+
url.path = ""
131+
url.fragment = nil
132+
url.query = nil
133+
134+
return url
135+
end
136+
137+
def client_for(endpoint)
138+
key = host_key(endpoint)
139+
140+
fetch(key) do
141+
make_client
142+
end
143+
end
137144
end
138145

146+
# An interface for creating and managing per-thread persistent HTTP clients.
139147
class PerThreadPersistentClients
148+
# Create a new instance of the class.
149+
#
150+
# @parameter options [Hash] The options to create the clients with.
151+
# @parameter block [Proc] An optional block to call with the client before it is used.
140152
def initialize(**options, &block)
141153
@options = options
142154
@block = block
143155

144156
@key = :"#{self.class}_#{object_id}"
145157
end
146158

159+
# Get a client for the given endpoint. If a client already exists for the host, it will be reused.
160+
#
161+
# The client instance will be will be cached per-thread.
162+
#
163+
# @yields {|client| ...} A client for the given endpoint.
147164
def with_client(endpoint, &block)
148165
clients.with_client(endpoint, &block)
149166
end
150167

168+
# Get a client for the given proxy endpoint and endpoint. If a client already exists for the host, it will be reused.
169+
#
170+
# The client instance will be will be cached per-thread.
171+
#
172+
# @parameter proxy_endpoint [IO::Endpoint::Generic] The proxy endpoint to use.
173+
# @parameter endpoint [IO::Endpoint::Generic] The endpoint to get the client for.
151174
def with_proxied_client(proxy_endpoint, endpoint, &block)
152175
clients.with_proxied_client(proxy_endpoint, endpoint, &block)
153176
end
154177

178+
# Close all clients.
179+
#
180+
# This will close all clients associated with all threads.
155181
def close
156182
Thread.list.each do |thread|
157183
if clients = thread[@key]
@@ -162,7 +188,7 @@ def close
162188
end
163189
end
164190

165-
protected
191+
private
166192

167193
def make_clients
168194
PersistentClients.new(**@options, &@block)

readme.md

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,17 @@
11
# Async::HTTP::Faraday
22

3-
Provides an adaptor for [Faraday](https://github.com/lostisland/faraday) to perform async HTTP requests. If you are designing a new library, you should probably just use `Async::HTTP::Client` directly.
3+
Provides an adaptor for [Faraday](https://github.com/lostisland/faraday) to perform async HTTP requests. If you are designing a new library, you should probably just use `Async::HTTP::Client` directly. However, for existing projects and libraries that use Faraday as an abstract interface, this can be a drop-in replacement to improve concurrency. It should be noted that the default `Net::HTTP` adapter works perfectly okay with Async, however it does not use persistent connections by default.
44

5-
[![Development Status](https://github.com/socketry/async-http-faraday/workflows/Test/badge.svg)](https://github.com/socketry/async-http-faraday/actions?workflow=Test)
6-
7-
## Installation
8-
9-
Add this line to your application's Gemfile:
10-
11-
``` ruby
12-
gem 'async-http-faraday'
13-
```
5+
- Persistent connections by default.
6+
- Supports HTTP/1 and HTTP/2 (and HTTP/3 in the future).
147

15-
And then execute:
16-
17-
$ bundle
18-
19-
Or install it yourself as:
20-
21-
$ gem install async-http-faraday
8+
[![Development Status](https://github.com/socketry/async-http-faraday/workflows/Test/badge.svg)](https://github.com/socketry/async-http-faraday/actions?workflow=Test)
229

2310
## Usage
2411

25-
Here is how you set faraday to use `Async::HTTP`:
26-
27-
``` ruby
28-
require 'async/http/faraday'
29-
30-
# Make it the global default:
31-
Faraday.default_adapter = :async_http
32-
33-
# Per connection:
34-
connection = Faraday.new(...) do |builder|
35-
builder.adapter :async_http
36-
end
37-
```
38-
39-
Here is how you make a request:
40-
41-
``` ruby
42-
Async do
43-
response = connection.get("/index")
44-
end
45-
```
46-
47-
### Default
48-
49-
To make this the default adaptor:
12+
Please see the [project documentation](https://socketry.github.io/async-http/) for more details.
5013

51-
``` ruby
52-
require 'async/http/faraday/default'
53-
```
14+
- [Getting Started](https://socketry.github.io/async-http/guides/getting-started/index) - This guide explains how to use use `Async::HTTP::Faraday` as a drop-in replacement for improved concurrency.
5415

5516
## Contributing
5617

0 commit comments

Comments
 (0)