This is the 12th day of my participation in the August More Text Challenge. For details, see:August is more challenging
preface
Jedis
Jedis is a Java connection development tool recommended by Redis. To use Redis middleware in Java development, you must have a good understanding of Jedis.
The basic use
Jedis is very simple to use. You only need to specify host,port, and password when creating Jedis objects.
Jedis jedis = new Jedis("ip",post); . jedis.close();Copy the code
After the Jedis object is created, the underlying layer of Jedis will open a Socket channel to connect to Redis. Therefore, after the Jedis object is used up, you need to jedis.close() to close the connection, otherwise it will occupy system resources. Having to manually create and destroy Jedis objects every time you use them can have a significant impact on the performance of your application, since the process of creating sockets is time-consuming. So we use connection pooling to reduce the creation and destruction of socket objects.
Connection pool usage
The Jedis connection pool is implemented by org.apache.com commons.pool2. When building the connection pool object, you need to provide the configuration object of the connection pool object. We can use this configuration object to configure the parameters of the connection pool, such as: maximum number of free connections, maximum number of connections.
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(8);
jedisPoolConfig.setMaxTotal(18);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "ip".6379.2000); Jedis jedis = jedisPool.getResource(); . jedis.close(); jedisPool.close();Copy the code
With Jedis connection pooling, the connection is returned to the pool each time the connection object is used up. Jedis’s implementation of the close() and getResource() methods.
//Jedis' close method
@Override
public void close(a) {
if(dataSource ! =null) {
if (client.isBroken()) {
this.dataSource.returnBrokenResource(this);
} else {
this.dataSource.returnResource(this); }}else{ client.close(); }}/ / JedisPool getResource () method
// The dataSource is set when the Jedis connection is obtained from the object pool
public Jedis getResource(a) {
Jedis jedis = super.getResource();
jedis.setDataSource(this);
return jedis;
}
Copy the code
High availability connection
We know that connection pooling can greatly improve the performance of applications accessing Reids services, minus a lot of Socket creation and destruction. However, in order to ensure high availability, Redis generally deployed in Sentinel mode. When the Master Service of Redis goes down, another Slaves will be arbitrated as Master. At this point, even if our application uses the Jedis connection pool and the Master service is down, we still cannot connect to the new Master service. In order to solve this problem, Jedis also provided a corresponding Sentinel implementation, which can notify our application when the Redis Sentinel Master/slave switch, and connect our application to the new Master service.
Set<String> sentinels = new HashSet<>();
sentinels.add("ip1");
sentinels.add("ip2");
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(8);
jedisPoolConfig.setMaxTotal(18);
JedisSentinelPool jedisPool = new JedisSentinelPool("mymaster", sentinels, jedisPoolConfig); Jedis jedis = jedisPool.getResource(); . jedis.close(); jedisPool.close();Copy the code
The use of JedisSentinelPool is simple, adding a set of set and masterName parameters to set the IP address of the server. The Underlying JedisSentinel is based on the Redis subscription to realize the switchover notification of the Redis master/slave service. When the switchover occurs, Sentinel sends a notification to proactively notify Jedis to switch connections. JedisSentinelPool checks the connection object every time it retrieves a connection from the pool. If the connection parameters are inconsistent with those of Sentinel’s Master service, it closes the connection and retrieves a new Jedis connection object.
@Override
public Jedis getResource(a) {
while (true) {
Jedis jedis = super.getResource();
jedis.setDataSource(this);
// get a reference because it can change concurrently
final HostAndPort master = currentHostMaster;
final HostAndPort connection = new HostAndPort(jedis.getClient().getHost(), jedis.getClient()
.getPort());
if (master.equals(connection)) {
// connected to the correct master
return jedis;
} else{ returnBrokenResource(jedis); }}}Copy the code
When you use Spring, you can introduce the Spring-data-Redis package, and when you use SpringBoot, you can refer directly to the Spring-boot-starter redis package, which is integrated with Jedis.
Of course, you can refer to the Jedis package separately.
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
Copy the code
Lettuce
Lettuce is an event-driven communication based on The Netty framework (NIO), supports synchronous and asynchronous calls, extensible Redis client, multiple threads can share a RedisConnection, thread safety.
The basic use
1. Encourage dependence
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
Copy the code
2. Lettuce operates Redis
// Synchronize operations
@Test
public void test(a) {
RedisURI redisURI = RedisURI.builder().withHost("ip").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient redisClient = RedisClient.create(redisURI);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisCommands<String, String> syncCommands = connection.sync();
String set = syncCommands.set("k1"."v1");
System.out.println(set);
connection.close();
redisClient.shutdown();
}
// Asynchronous operation
@Test
public void testAsync(a){
RedisURI redisURI = RedisURI.builder().withHost("ip").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient redisClient = RedisClient.create(redisURI);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisAsyncCommands<String, String> asyncCommands = connection.async();
RedisFuture<String> set = asyncCommands.set("k2"."v2");
System.out.println(set);
connection.close();
redisClient.shutdown();
}
// Reactive API
@Test
public void testReactive(a) {
RedisURI redisURI = RedisURI.builder().withHost("192.168.96.173").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient redisClient = RedisClient.create(redisURI);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
Mono<String> set = reactiveCommands.set("name"."feiyangyang");
System.out.println(set.block());
// Set the expiration time
SetArgs args = SetArgs.Builder.nx().ex(10);
set = reactiveCommands.set("age"."19", args);
set.subscribe(System.out::println);
// To start a transaction, set count to 1 and increment by 1
reactiveCommands.multi().doOnSuccess(r -> {
reactiveCommands.set("count"."1").doOnNext(System.out::println).subscribe();
reactiveCommands.incr("count").doOnNext(System.out::println).subscribe();
}).flatMap(s -> reactiveCommands.exec())
.doOnNext(transactionResult -> System.out.println(transactionResult.wasDiscarded())).subscribe();
}
Copy the code
High availability connection
/ / master-slave
@Test
public void masterSlave(a) {
RedisURI redisURI = RedisURI.builder().withHost("ip").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();
RedisClient client = RedisClient.create(redisURI);
StatefulRedisMasterReplicaConnection<String, String> connection = MasterReplica.connect(client, StringCodec.UTF8, redisURI);
// The secondary node reads data from the primary node
connection.setReadFrom(ReadFrom.REPLICA);
RedisCommands<String, String> commands = connection.sync();
commands.set("name"."feiyangyang");
System.out.println(commands.get("name"));
connection.close();
client.shutdown();
}
/ / the sentry
@Test
public void sentinel(a) {
List<RedisURI> uris = new ArrayList();
uris.add(RedisURI.builder().withSentinel("ip1".26379).withSentinelMasterId("mymaster").withPassword("123456").build());
uris.add(RedisURI.builder().withSentinel("ip2".26379).withSentinelMasterId("mymaster").withPassword("123456").build());
uris.add(RedisURI.builder().withSentinel("ip3".26379).withSentinelMasterId("mymaster").withPassword("123456").build());
RedisClient client = RedisClient.create();
StatefulRedisMasterReplicaConnection<String, String> connection = MasterReplica.connect(client, StringCodec.UTF8, uris);
connection.setReadFrom(ReadFrom.REPLICA);
RedisCommands<String, String> commands = connection.sync();
commands.set("name"."feiyangyang");
System.out.println(commands.get("name"));
connection.close();
client.shutdown();
}
/ / cluster
@Test
public void cluster(a) {
Set<RedisURI> uris = new HashSet<>();
uris.add(RedisURI.builder().withHost("ip1").withPort(7000).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip2").withPort(7001).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip3").withPort(7000).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip4").withPort(7001).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip5").withPort(7000).withPassword("123456").build());
uris.add(RedisURI.builder().withHost("ip6").withPort(7001).withPassword("123456").build());
RedisClusterClient client = RedisClusterClient.create(uris);
StatefulRedisClusterConnection<String, String> connection = client.connect();
RedisAdvancedClusterCommands<String, String> commands = connection.sync();
commands.set("name"."feiyangyang");
System.out.println(commands.get("name"));
// Select the secondary node and read only
NodeSelection<String, String> replicas = commands.replicas();
NodeSelectionCommands<String, String> nodeSelectionCommands = replicas.commands();
Executions<List<String>> keys = nodeSelectionCommands.keys("*");
keys.forEach(key -> System.out.println(key));
connection.close();
client.shutdown();
}
Copy the code
conclusion
Jedis is directly connected to Redis Server, which can cause thread safety issues. Add physical connections for each Jedis instance unless you are using connection pooling.
Advantages:
- Easy to understand
- Comprehensive Redis operation API
Disadvantages:
- Synchronous blocking IO
- Asynchrony not supported
- Thread unsafe
Lettuce is based on Netty and connection instances can be accessed concurrently among multiple threads. Lettuce also supports asynchronous connection to improve network waiting and disk IO efficiency.
Advantages:
- Thread safety
- Event-driven communication based on Netty framework can be called asynchronously
- Applicable to distributed caches
Disadvantages:
- The cost of learning is high and it is relatively complicated to get started