diff --git a/content/develop/connect/clients/dotnet.md b/content/develop/connect/clients/dotnet.md index 5eba0d70d5..d007f8ffaa 100644 --- a/content/develop/connect/clients/dotnet.md +++ b/content/develop/connect/clients/dotnet.md @@ -37,7 +37,7 @@ dotnet add package NRedisStack Connect to localhost on port 6379. -``` +```csharp using NRedisStack; using NRedisStack.RedisStackCommands; using StackExchange.Redis; @@ -70,7 +70,7 @@ Console.WriteLine(String.Join("; ", hashFields)); // name: John; surname: Smith; company: Redis; age: 29 ``` -To access Redis Stack capabilities, you should use appropriate interface like this: +To access Redis Stack capabilities, use the appropriate interface like this: ``` IBloomCommands bf = db.BF(); @@ -84,7 +84,7 @@ IJsonCommands json = db.JSON(); ITimeSeriesCommands ts = db.TS(); ``` -### Connect to a Redis cluster +## Connect to a Redis cluster To connect to a Redis cluster, you just need to specify one or all cluster endpoints in the client configuration: @@ -106,7 +106,7 @@ db.StringSet("foo", "bar"); Console.WriteLine(db.StringGet("foo")); // prints bar ``` -### Connect to your production Redis with TLS +## Connect to your production Redis with TLS When you deploy your application, use TLS and follow the [Redis security]({{< relref "/operate/oss_and_stack/management/security/" >}}) guidelines. @@ -169,6 +169,23 @@ conn.StringSet("foo", "bar"); Console.WriteLine(conn.StringGet("foo")); ``` +## Multiplexing + +Although example code typically works with a single connection, +real-world code often uses multiple connections at the same time. +Opening and closing connections repeatedly is inefficient, so it is best +to manage open connections carefully to avoid this. + +Several other +Redis client libraries use *connection pools* to reuse a set of open +connections efficiently. NRedisStack uses a different approach called +*multiplexing*, which sends all client commands and responses over a +single connection. NRedisStack manages multiplexing for you automatically. +This gives high performance without requiring any extra coding. +See +[Connection pools and multiplexing]({{< relref "/develop/connect/clients/pools-and-muxing" >}}) +for more information. + ## Example: Indexing and querying JSON documents This example shows how to convert Redis search results to JSON format using `NRedisStack`. diff --git a/content/develop/connect/clients/java/jedis.md b/content/develop/connect/clients/java/jedis.md index ad8ed6c6ab..cd8c76e9ad 100644 --- a/content/develop/connect/clients/java/jedis.md +++ b/content/develop/connect/clients/java/jedis.md @@ -56,46 +56,36 @@ To include `Jedis` as a dependency in your application, edit the dependency file ## Connect -For many applications, it's best to use a connection pool. You can instantiate and use a `Jedis` connection pool like so: +The following code opens a basic connection to a local Redis server: ```java package org.example; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; +import redis.clients.jedis.UnifiedJedis; public class Main { public static void main(String[] args) { - JedisPool pool = new JedisPool("localhost", 6379); + UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379"); - try (Jedis jedis = pool.getResource()) { - // Store & Retrieve a simple string - jedis.set("foo", "bar"); - System.out.println(jedis.get("foo")); // prints bar - - // Store & Retrieve a HashMap - Map hash = new HashMap<>();; - hash.put("name", "John"); - hash.put("surname", "Smith"); - hash.put("company", "Redis"); - hash.put("age", "29"); - jedis.hset("user-session:123", hash); - System.out.println(jedis.hgetAll("user-session:123")); - // Prints: {name=John, surname=Smith, company=Redis, age=29} - } + // Code that interacts with Redis... + + jedis.close(); } } ``` -Because adding a `try-with-resources` block for each command can be cumbersome, consider using `JedisPooled` as an easier way to pool connections. +After you have connected, you can check the connection by storing and +retrieving a simple string value: ```java -import redis.clients.jedis.JedisPooled; +... -//... +String res1 = jedis.set("bike:1", "Deimos"); +System.out.println(res1); // OK -JedisPooled jedis = new JedisPooled("localhost", 6379); -jedis.set("foo", "bar"); -System.out.println(jedis.get("foo")); // prints "bar" +String res2 = jedis.get("bike:1"); +System.out.println(res2); // Deimos + +... ``` ### Connect to a Redis cluster @@ -313,15 +303,61 @@ The client will also flush the cache automatically if any connection (including one from a connection pool) is disconnected. -## Production usage +## Connect with a connection pool -The following sections explain how to handle situations that may occur -in your production environment. +For production usage, you should use a connection pool to manage +connections rather than opening and closing connections individually. +A connection pool maintains several open connections and reuses them +efficiently. When you open a connection from a pool, the pool allocates +one of its open connections. When you subsequently close the same connection, +it is not actually closed but simply returned to the pool for reuse. +This avoids the overhead of repeated connecting and disconnecting. +See +[Connection pools and multiplexing]({{< relref "/develop/connect/clients/pools-and-muxing" >}}) +for more information. + +Use the following code to connect with a connection pool: + +```java +package org.example; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +public class Main { + public static void main(String[] args) { + JedisPool pool = new JedisPool("localhost", 6379); + + try (Jedis jedis = pool.getResource()) { + // Store & Retrieve a simple string + jedis.set("foo", "bar"); + System.out.println(jedis.get("foo")); // prints bar + + // Store & Retrieve a HashMap + Map hash = new HashMap<>();; + hash.put("name", "John"); + hash.put("surname", "Smith"); + hash.put("company", "Redis"); + hash.put("age", "29"); + jedis.hset("user-session:123", hash); + System.out.println(jedis.hgetAll("user-session:123")); + // Prints: {name=John, surname=Smith, company=Redis, age=29} + } + } +} +``` + +Because adding a `try-with-resources` block for each command can be cumbersome, consider using `JedisPooled` as an easier way to pool connections. `JedisPooled`, added in Jedis version 4.0.0, provides capabilities similar to `JedisPool` but with a more straightforward API. + +```java +import redis.clients.jedis.JedisPooled; + +//... -### Configuring a connection pool +JedisPooled jedis = new JedisPooled("localhost", 6379); +jedis.set("foo", "bar"); +System.out.println(jedis.get("foo")); // prints "bar" +``` -As mentioned in the previous section, use `JedisPool` or `JedisPooled` to create a connection pool. -`JedisPooled`, added in Jedis version 4.0.0, provides capabilities similar to `JedisPool` but with a more straightforward API. A connection pool holds a specified number of connections, creates more connections when necessary, and terminates them when they are no longer needed. Here is a simplified connection lifecycle in a pool: @@ -367,6 +403,11 @@ poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1)); JedisPooled jedis = new JedisPooled(poolConfig, "localhost", 6379); ``` +## Production usage + +The following sections explain how to handle situations that may occur +in your production environment. + ### Timeouts To set a timeout for a connection, use the `JedisPooled` or `JedisPool` constructor with the `timeout` parameter, or use `JedisClientConfig` with the `socketTimeout` and `connectionTimeout` parameters: diff --git a/content/develop/connect/clients/java/lettuce.md b/content/develop/connect/clients/java/lettuce.md index 9636dd7655..315a89a72e 100644 --- a/content/develop/connect/clients/java/lettuce.md +++ b/content/develop/connect/clients/java/lettuce.md @@ -234,6 +234,9 @@ try (RedisClient client = RedisClient.create(redisURI)) { A typical approach with Lettuce is to create a single `RedisClient` instance and reuse it to establish connections to your Redis server(s). These connections are multiplexed; that is, multiple commands can be run concurrently over a single or a small set of connections, making explicit pooling less practical. +See +[Connection pools and multiplexing]({{< relref "/develop/connect/clients/pools-and-muxing" >}}) +for more information. Lettuce provides pool config to be used with Lettuce asynchronous connection methods. diff --git a/content/develop/connect/clients/pools-and-muxing.md b/content/develop/connect/clients/pools-and-muxing.md new file mode 100644 index 0000000000..82725e34bf --- /dev/null +++ b/content/develop/connect/clients/pools-and-muxing.md @@ -0,0 +1,80 @@ +--- +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +description: Manage Redis connections efficiently +linkTitle: Pooling/multiplexing +title: Connection pools and multiplexing +weight: 10 +--- + +Redis example code generally opens a connection, demonstrates +a command or feature, and then closes. Real-world code typically +has short bursts of communication with the server and periods of +inactivity in between. Opening and closing connections +involves some overhead and leads to inefficiency if you do +it frequently. This means that you can improve the performance of production +code by making as few separate connections as possible. + +Managing connections in your own code can be tricky, so the Redis +client libraries give you some help. The two basic approaches to +connection management are called *connection pooling* and *multiplexing*. +The [`redis-py`]({{< relref "/develop/connect/clients/python/redis-py" >}}), +[`jedis`]({{< relref "/develop/connect/clients/java/jedis" >}}), and +[`go-redis`]({{< relref "/develop/connect/clients/go" >}}) clients support +connection pooling, while +[`NRedisStack`]({{< relref "/develop/connect/clients/dotnet" >}}) +supports multiplexing. +[`Lettuce`]({{< relref "/develop/connect/clients/java/lettuce" >}}) +supports both approaches. + +## Connection pooling + +When you initialize a connection pool, the client opens a small number +of connections and adds them to the pool. + +{{< image filename="/images/dev/connect/pool-and-mux/ConnPoolInit.drawio.svg" >}} + +Each time you "open" a connection +from the pool, the client returns one of these existing +connections and notes the fact that it is in use. + +{{< image filename="/images/dev/connect/pool-and-mux/ConnPoolInUse.drawio.svg" >}} + +When you later "close" +the connection, the client puts it back into the pool of available +connections without actually closing it. + +{{< image filename="/images/dev/connect/pool-and-mux/ConnPoolDiscon.drawio.svg" >}} + +If all connections in the pool are in use but the app needs more, then +the client can simply open new connections as necessary. In this way, the client +eventually finds the right number of connections to satisfy your +app's demands. + +## Multiplexing + +Instead of pooling several connections, a multiplexer keeps a +single connection open and uses it for all traffic between the +client and the server. The "connections" returned to your code are +used to identify where to send the response data from your commands. + +{{< image filename="/images/dev/connect/pool-and-mux/ConnMux.drawio.svg" >}} + +Note that it is not a problem if the multiplexer receives several commands close +together in time. When this happens, the multiplexer can often combine the commands into a +[pipeline]({{< relref "/develop/use/pipelining" >}}), which +improves efficiency. + +Multiplexing offers high efficiency but works transparently without requiring +any special code to enable it in your app. The main disadvantage of multiplexing compared to +connection pooling is that it can't support the blocking "pop" commands (such as +[`BLPOP`]({{< relref "/commands/blpop" >}})) since these would stall the +connection for all callers. diff --git a/content/develop/connect/clients/python/redis-py.md b/content/develop/connect/clients/python/redis-py.md index afa2df40b7..340a741cfa 100644 --- a/content/develop/connect/clients/python/redis-py.md +++ b/content/develop/connect/clients/python/redis-py.md @@ -232,6 +232,40 @@ The client will also flush the cache automatically if any connection (including one from a connection pool) is disconnected. +## Connect with a connection pool + +For production usage, you should use a connection pool to manage +connections rather than opening and closing connections individually. +A connection pool maintains several open connections and reuses them +efficiently. When you open a connection from a pool, the pool allocates +one of its open connections. When you subsequently close the same connection, +it is not actually closed but simply returned to the pool for reuse. +This avoids the overhead of repeated connecting and disconnecting. +See +[Connection pools and multiplexing]({{< relref "/develop/connect/clients/pools-and-muxing" >}}) +for more information. + +Use the following code to connect with a connection pool: + +```python +import redis + +pool = redis.ConnectionPool().from_url("redis://localhost") +r1 = redis.Redis().from_pool(pool) +r2 = redis.Redis().from_pool(pool) +r3 = redis.Redis().from_pool(pool) + +r1.set("wind:1", "Hurricane") +r2.set("wind:2", "Tornado") +r3.set("wind:3", "Mistral") + +r1.close() +r2.close() +r3.close() + +pool.close() +``` + ## Example: Indexing and querying JSON documents Make sure that you have Redis Stack and `redis-py` installed. Import dependencies: diff --git a/static/images/dev/connect/pool-and-mux/ConnMux.drawio.svg b/static/images/dev/connect/pool-and-mux/ConnMux.drawio.svg new file mode 100644 index 0000000000..6a876f915b --- /dev/null +++ b/static/images/dev/connect/pool-and-mux/ConnMux.drawio.svg @@ -0,0 +1,317 @@ + + + + + + + + + + + + + +
+
+
+ Caller 1 +
+
+
+
+ + Caller 1 + +
+
+ + + +
+
+
+ Connected +
+
+
+
+ + Connected + +
+
+ + + + + + +
+
+
+ Caller 2 +
+
+
+
+ + Caller 2 + +
+
+ + + +
+
+
+ Connected +
+
+
+
+ + Connected + +
+
+ + + + + + +
+
+
+ Multiplexed connection +
+
+
+
+ + Multiplexed connection + +
+
+ + + + +
+
+
+ SET +
+
+
+
+ + SET + +
+
+ + + + +
+
+
+ GET +
+
+
+
+ + GET + +
+
+ + + + +
+
+
+ ... +
+
+
+
+ + ... + +
+
+ + + +
+
+
+ Commands > +
+
+
+
+ + Commands > + +
+
+ + + + +
+
+
+ R1 +
+
+
+
+ + R1 + +
+
+ + + + +
+
+
+ R2 +
+
+
+
+ + R2 + +
+
+ + + +
+
+
+ < Responses +
+
+
+
+ + < Responses + +
+
+ + + + +
+
+
+ ... +
+
+
+
+ + ... + +
+
+ + + + + + +
+
+
+ SET +
+
+
+
+ + SET + +
+
+ + + + +
+
+
+ GET +
+
+
+
+ + GET + +
+
+ + + + + + + + + +
+
+
+ R1 +
+
+
+
+ + R1 + +
+
+ + + + + + +
+
+
+ R2 +
+
+
+
+ + R2 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/static/images/dev/connect/pool-and-mux/ConnPoolDiscon.drawio.svg b/static/images/dev/connect/pool-and-mux/ConnPoolDiscon.drawio.svg new file mode 100644 index 0000000000..3deb8474de --- /dev/null +++ b/static/images/dev/connect/pool-and-mux/ConnPoolDiscon.drawio.svg @@ -0,0 +1,4 @@ + + + +
Connection pool
Connection 1
Connection 2
Connection 3
...
Connection n
Caller
Connected
Caller
<Not connected>
Caller
Disconnecting...
\ No newline at end of file diff --git a/static/images/dev/connect/pool-and-mux/ConnPoolInUse.drawio.svg b/static/images/dev/connect/pool-and-mux/ConnPoolInUse.drawio.svg new file mode 100644 index 0000000000..fd4aa31abc --- /dev/null +++ b/static/images/dev/connect/pool-and-mux/ConnPoolInUse.drawio.svg @@ -0,0 +1,4 @@ + + + +
Connection pool
Connection 1
Connection 2
Connection 3
...
Connection n
Caller
 Connecting...
Caller
<Not connected>
Caller
Connecting...
\ No newline at end of file diff --git a/static/images/dev/connect/pool-and-mux/ConnPoolInit.drawio.svg b/static/images/dev/connect/pool-and-mux/ConnPoolInit.drawio.svg new file mode 100644 index 0000000000..eb1e9259e8 --- /dev/null +++ b/static/images/dev/connect/pool-and-mux/ConnPoolInit.drawio.svg @@ -0,0 +1,4 @@ + + + +
Connection pool
Connection 1
Connection 2
Connection 3
...
Connection n
Caller
<Not connected>
Caller
<Not connected>
Caller
<Not connected>
\ No newline at end of file